xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 94c7d993a32df8cdb1f82e3a0dcff15872c65a16)
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;
1096d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1106d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1111e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1121e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
113b2fc195eSAndrew Gallatin 
1146d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1156d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1166d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1176d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1186d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
119b2fc195eSAndrew Gallatin 
1206d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
121b2fc195eSAndrew Gallatin {
122b2fc195eSAndrew Gallatin   /* Device interface */
1236d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1246d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1256d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1266d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
127b2fc195eSAndrew Gallatin   {0, 0}
128b2fc195eSAndrew Gallatin };
129b2fc195eSAndrew Gallatin 
1306d87a65dSAndrew Gallatin static driver_t mxge_driver =
131b2fc195eSAndrew Gallatin {
1326d87a65dSAndrew Gallatin   "mxge",
1336d87a65dSAndrew Gallatin   mxge_methods,
1346d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
135b2fc195eSAndrew Gallatin };
136b2fc195eSAndrew Gallatin 
1376d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
138b2fc195eSAndrew Gallatin 
139b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1406d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1416d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
142f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
143b2fc195eSAndrew Gallatin 
1441e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1458fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
146276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
147276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
148276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1498fe615baSAndrew Gallatin 
150b2fc195eSAndrew Gallatin static int
1516d87a65dSAndrew Gallatin mxge_probe(device_t dev)
152b2fc195eSAndrew Gallatin {
15301638550SAndrew Gallatin 	int rev;
15401638550SAndrew Gallatin 
15501638550SAndrew Gallatin 
1566d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
157f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
158f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
15901638550SAndrew Gallatin 		rev = pci_get_revid(dev);
16001638550SAndrew Gallatin 		switch (rev) {
16101638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
162b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16301638550SAndrew Gallatin 			break;
16401638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
16501638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
16601638550SAndrew Gallatin 			break;
16701638550SAndrew Gallatin 		default:
16801638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
16901638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
17001638550SAndrew Gallatin 				      rev);
17101638550SAndrew Gallatin 			break;
17201638550SAndrew Gallatin 		}
173b2fc195eSAndrew Gallatin 		return 0;
174b2fc195eSAndrew Gallatin 	}
175b2fc195eSAndrew Gallatin 	return ENXIO;
176b2fc195eSAndrew Gallatin }
177b2fc195eSAndrew Gallatin 
178b2fc195eSAndrew Gallatin static void
1796d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
180b2fc195eSAndrew Gallatin {
181f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
182b2fc195eSAndrew Gallatin 	vm_offset_t len;
18347c2e987SAndrew Gallatin 	int err;
184b2fc195eSAndrew Gallatin 
1854d69a9d0SAndrew Gallatin 	sc->wc = 1;
186b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
187c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
188c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
18947c2e987SAndrew Gallatin 	if (err != 0) {
190c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
191c2c14a69SAndrew Gallatin 			      err);
1924d69a9d0SAndrew Gallatin 		sc->wc = 0;
193b2fc195eSAndrew Gallatin 	}
194f9ae0280SAndrew Gallatin #endif
195b2fc195eSAndrew Gallatin }
196b2fc195eSAndrew Gallatin 
197b2fc195eSAndrew Gallatin 
198b2fc195eSAndrew Gallatin /* callback to get our DMA address */
199b2fc195eSAndrew Gallatin static void
2006d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
201b2fc195eSAndrew Gallatin 			 int error)
202b2fc195eSAndrew Gallatin {
203b2fc195eSAndrew Gallatin 	if (error == 0) {
204b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
205b2fc195eSAndrew Gallatin 	}
206b2fc195eSAndrew Gallatin }
207b2fc195eSAndrew Gallatin 
208b2fc195eSAndrew Gallatin static int
2096d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
210b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
211b2fc195eSAndrew Gallatin {
212b2fc195eSAndrew Gallatin 	int err;
213b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2141e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2151e413cf9SAndrew Gallatin 
2161e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2171e413cf9SAndrew Gallatin 		boundary = 0;
2181e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2191e413cf9SAndrew Gallatin 	} else {
2201e413cf9SAndrew Gallatin 		boundary = 4096;
2211e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2221e413cf9SAndrew Gallatin 	}
223b2fc195eSAndrew Gallatin 
224b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
225b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
226b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2271e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
228b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
229b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
230b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
231b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
232b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2331e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
234b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
235b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
236b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
237b2fc195eSAndrew Gallatin 	if (err != 0) {
238b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
239b2fc195eSAndrew Gallatin 		return err;
240b2fc195eSAndrew Gallatin 	}
241b2fc195eSAndrew Gallatin 
242b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
243b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
244b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
245b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
246b2fc195eSAndrew Gallatin 	if (err != 0) {
247b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
248b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
249b2fc195eSAndrew Gallatin 	}
250b2fc195eSAndrew Gallatin 
251b2fc195eSAndrew Gallatin 	/* load the memory */
252b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2536d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
254b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
255b2fc195eSAndrew Gallatin 	if (err != 0) {
256b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
257b2fc195eSAndrew Gallatin 		goto abort_with_mem;
258b2fc195eSAndrew Gallatin 	}
259b2fc195eSAndrew Gallatin 	return 0;
260b2fc195eSAndrew Gallatin 
261b2fc195eSAndrew Gallatin abort_with_mem:
262b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
263b2fc195eSAndrew Gallatin abort_with_dmat:
264b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
265b2fc195eSAndrew Gallatin 	return err;
266b2fc195eSAndrew Gallatin }
267b2fc195eSAndrew Gallatin 
268b2fc195eSAndrew Gallatin 
269b2fc195eSAndrew Gallatin static void
2706d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
271b2fc195eSAndrew Gallatin {
272b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
273b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
274b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
275b2fc195eSAndrew Gallatin }
276b2fc195eSAndrew Gallatin 
277b2fc195eSAndrew Gallatin /*
278b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
279b2fc195eSAndrew Gallatin  * SN=x\0
280b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
281b2fc195eSAndrew Gallatin  * PC=text\0
282b2fc195eSAndrew Gallatin  */
283b2fc195eSAndrew Gallatin 
284b2fc195eSAndrew Gallatin static int
2856d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
286b2fc195eSAndrew Gallatin {
2876d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
288b2fc195eSAndrew Gallatin 
289b2fc195eSAndrew Gallatin 	char *ptr, *limit;
290b2fc195eSAndrew Gallatin 	int i, found_mac;
291b2fc195eSAndrew Gallatin 
292b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2936d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
294b2fc195eSAndrew Gallatin 	found_mac = 0;
295b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
296b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2975e7d8541SAndrew Gallatin 			ptr += 1;
298b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
299b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
3005e7d8541SAndrew Gallatin 				ptr += 3;
301b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
302b2fc195eSAndrew Gallatin 					goto abort;
303b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
304b2fc195eSAndrew Gallatin 				found_mac = 1;
305b2fc195eSAndrew Gallatin 			}
3065e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
3075e7d8541SAndrew Gallatin 			ptr += 3;
3085e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3095e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3105e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3115e7d8541SAndrew Gallatin 			ptr += 3;
3125e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3135e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
314b2fc195eSAndrew Gallatin 		}
3156d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
316b2fc195eSAndrew Gallatin 	}
317b2fc195eSAndrew Gallatin 
318b2fc195eSAndrew Gallatin 	if (found_mac)
319b2fc195eSAndrew Gallatin 		return 0;
320b2fc195eSAndrew Gallatin 
321b2fc195eSAndrew Gallatin  abort:
322b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
323b2fc195eSAndrew Gallatin 
324b2fc195eSAndrew Gallatin 	return ENXIO;
325b2fc195eSAndrew Gallatin }
326b2fc195eSAndrew Gallatin 
3270d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3288fe615baSAndrew Gallatin static void
3298fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
330b2fc195eSAndrew Gallatin {
331b2fc195eSAndrew Gallatin 	uint32_t val;
3328fe615baSAndrew Gallatin 	unsigned long base, off;
333b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3348fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3358fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
336b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
337b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
338b2fc195eSAndrew Gallatin 
3398fe615baSAndrew Gallatin 
3408fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3418fe615baSAndrew Gallatin 		return;
3428fe615baSAndrew Gallatin 
3438fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3448fe615baSAndrew Gallatin 	if (pdev == NULL) {
3458fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3468fe615baSAndrew Gallatin 		return;
3478fe615baSAndrew Gallatin 	}
3488fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3498fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3508fe615baSAndrew Gallatin 
3518fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3528fe615baSAndrew Gallatin 		return;
3538fe615baSAndrew Gallatin 
3548fe615baSAndrew Gallatin 	base = 0;
3558fe615baSAndrew Gallatin 
3568fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3578fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3588fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3598fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3608fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3618fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3628fe615baSAndrew Gallatin 		if (mcp55 &&
3638fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3648fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3658fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3668fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3678fe615baSAndrew Gallatin 		}
3688fe615baSAndrew Gallatin 	}
3698fe615baSAndrew Gallatin 	if (!base)
3708fe615baSAndrew Gallatin 		return;
3718fe615baSAndrew Gallatin 
372b2fc195eSAndrew Gallatin 	/* XXXX
373b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
374b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
375b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
376b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
377b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
378b2fc195eSAndrew Gallatin 	*/
379b2fc195eSAndrew Gallatin #if 0
380b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
381b2fc195eSAndrew Gallatin 	   config space */
382b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
383b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
384b2fc195eSAndrew Gallatin 		val |= 0x40;
385b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3868fe615baSAndrew Gallatin 		return;
387b2fc195eSAndrew Gallatin 	}
388b2fc195eSAndrew Gallatin #endif
389b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
390b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
391b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
392b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
393b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
394b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
395b2fc195eSAndrew Gallatin 	 */
396b2fc195eSAndrew Gallatin 
397b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
398b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
399b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
400b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
401b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
402b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
403b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
404b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
405b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
406b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
407b2fc195eSAndrew Gallatin 
4088fe615baSAndrew Gallatin 	off =  base
409b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
410b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
411b2fc195eSAndrew Gallatin 						 + 8 * slot);
412b2fc195eSAndrew Gallatin 
413b2fc195eSAndrew Gallatin 	/* map it into the kernel */
414b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
415b2fc195eSAndrew Gallatin 
416b2fc195eSAndrew Gallatin 
417b2fc195eSAndrew Gallatin 	if (va == NULL) {
418b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4198fe615baSAndrew Gallatin 		return;
420b2fc195eSAndrew Gallatin 	}
421b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
422b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
423b2fc195eSAndrew Gallatin 
424b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
425b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
426b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
427b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
428b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
429b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
430b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4318fe615baSAndrew Gallatin 		return;
432b2fc195eSAndrew Gallatin 	}
433b2fc195eSAndrew Gallatin 
434b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
435b2fc195eSAndrew Gallatin 	val = *ptr32;
436b2fc195eSAndrew Gallatin 
437b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
438b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
439b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4408fe615baSAndrew Gallatin 		return;
441b2fc195eSAndrew Gallatin 	}
442b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
443b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4445e7d8541SAndrew Gallatin 	if (mxge_verbose)
445b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4465e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4475e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
448b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4498fe615baSAndrew Gallatin 	return;
450b2fc195eSAndrew Gallatin }
451b2fc195eSAndrew Gallatin #else
4528fe615baSAndrew Gallatin static void
453f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
454b2fc195eSAndrew Gallatin {
455b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
456b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4578fe615baSAndrew Gallatin 	return;
458b2fc195eSAndrew Gallatin }
459b2fc195eSAndrew Gallatin #endif
4608fe615baSAndrew Gallatin 
4618fe615baSAndrew Gallatin 
4628fe615baSAndrew Gallatin static int
4638fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4648fe615baSAndrew Gallatin {
4658fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4668fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4678fe615baSAndrew Gallatin 	int status;
4688fe615baSAndrew Gallatin 	uint32_t len;
4698fe615baSAndrew Gallatin 	char *test = " ";
4708fe615baSAndrew Gallatin 
4718fe615baSAndrew Gallatin 
4728fe615baSAndrew Gallatin 	/* Run a small DMA test.
4738fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4748fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4758fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4768fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4778fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4788fe615baSAndrew Gallatin 	 * transfers took to complete.
4798fe615baSAndrew Gallatin 	 */
4808fe615baSAndrew Gallatin 
4811e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4828fe615baSAndrew Gallatin 
4838fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4848fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4858fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4868fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4878fe615baSAndrew Gallatin 	if (status != 0) {
4888fe615baSAndrew Gallatin 		test = "read";
4898fe615baSAndrew Gallatin 		goto abort;
4908fe615baSAndrew Gallatin 	}
4918fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4928fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4938fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4948fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4958fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4968fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4978fe615baSAndrew Gallatin 	if (status != 0) {
4988fe615baSAndrew Gallatin 		test = "write";
4998fe615baSAndrew Gallatin 		goto abort;
5008fe615baSAndrew Gallatin 	}
5018fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5028fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5038fe615baSAndrew Gallatin 
5048fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5058fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5068fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5078fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5088fe615baSAndrew Gallatin 	if (status != 0) {
5098fe615baSAndrew Gallatin 		test = "read/write";
5108fe615baSAndrew Gallatin 		goto abort;
5118fe615baSAndrew Gallatin 	}
5128fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5138fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5148fe615baSAndrew Gallatin 
5158fe615baSAndrew Gallatin abort:
5168fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5178fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5188fe615baSAndrew Gallatin 			      test, status);
5198fe615baSAndrew Gallatin 
5208fe615baSAndrew Gallatin 	return status;
5218fe615baSAndrew Gallatin }
5228fe615baSAndrew Gallatin 
523b2fc195eSAndrew Gallatin /*
524b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
525b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
526b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
527b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
528b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
529b2fc195eSAndrew Gallatin  *
530b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
531b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
532b2fc195eSAndrew Gallatin  *
533b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
534b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
535b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
536b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5371e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
538b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5391e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
540b2fc195eSAndrew Gallatin  */
541b2fc195eSAndrew Gallatin 
5428fe615baSAndrew Gallatin static int
5438fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5448fe615baSAndrew Gallatin {
5458fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5468fe615baSAndrew Gallatin 	int reg, status;
5478fe615baSAndrew Gallatin 	uint16_t pectl;
5488fe615baSAndrew Gallatin 
5491e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5508fe615baSAndrew Gallatin 	/*
5518fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5528fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5538fe615baSAndrew Gallatin 	 */
5548fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5558fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5568fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5578fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5588fe615baSAndrew Gallatin 				      pectl);
5591e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5608fe615baSAndrew Gallatin 		}
5618fe615baSAndrew Gallatin 	}
5628fe615baSAndrew Gallatin 
5638fe615baSAndrew Gallatin 	/*
5648fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5658fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5668fe615baSAndrew Gallatin 	 */
5678fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5681e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5698fe615baSAndrew Gallatin 	if (status != 0) {
5708fe615baSAndrew Gallatin 		return status;
5718fe615baSAndrew Gallatin 	}
5728fe615baSAndrew Gallatin 
5738fe615baSAndrew Gallatin 	/*
5748fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5758fe615baSAndrew Gallatin 	 */
5768fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5778fe615baSAndrew Gallatin 
5788fe615baSAndrew Gallatin 	/*
5798fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5808fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5818fe615baSAndrew Gallatin 	 */
5828fe615baSAndrew Gallatin 
5838fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5848fe615baSAndrew Gallatin 	if (status == 0)
5858fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5868fe615baSAndrew Gallatin 
5878fe615baSAndrew Gallatin 	if (status != E2BIG)
5888fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5898fe615baSAndrew Gallatin 	if (status == ENOSYS)
5908fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5918fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5928fe615baSAndrew Gallatin 	return status;
5938fe615baSAndrew Gallatin }
5948fe615baSAndrew Gallatin 
5958fe615baSAndrew Gallatin static int
5966d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
597b2fc195eSAndrew Gallatin {
5988fe615baSAndrew Gallatin 	int aligned = 0;
599b2fc195eSAndrew Gallatin 
600d91b1b49SAndrew Gallatin 
601d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
602d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
603d91b1b49SAndrew Gallatin 			aligned = 1;
604d91b1b49SAndrew Gallatin 		else
605d91b1b49SAndrew Gallatin 			aligned = 0;
606d91b1b49SAndrew Gallatin 		if (mxge_verbose)
607d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
608d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
609d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
610d91b1b49SAndrew Gallatin 		goto abort;
611d91b1b49SAndrew Gallatin 	}
612d91b1b49SAndrew Gallatin 
613d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
614d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
615d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
616d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
617d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
618d91b1b49SAndrew Gallatin 			      sc->link_width);
619d91b1b49SAndrew Gallatin 		aligned = 1;
620d91b1b49SAndrew Gallatin 		goto abort;
621d91b1b49SAndrew Gallatin 	}
622d91b1b49SAndrew Gallatin 
6238fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6248fe615baSAndrew Gallatin 		return 0;
625b2fc195eSAndrew Gallatin 
626b2fc195eSAndrew Gallatin abort:
627b2fc195eSAndrew Gallatin 	if (aligned) {
6286d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6291e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
630b2fc195eSAndrew Gallatin 	} else {
6316d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6321e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
633b2fc195eSAndrew Gallatin 	}
6341e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
635b2fc195eSAndrew Gallatin }
636b2fc195eSAndrew Gallatin 
637b2fc195eSAndrew Gallatin union qualhack
638b2fc195eSAndrew Gallatin {
639b2fc195eSAndrew Gallatin         const char *ro_char;
640b2fc195eSAndrew Gallatin         char *rw_char;
641b2fc195eSAndrew Gallatin };
642b2fc195eSAndrew Gallatin 
6434da0d523SAndrew Gallatin static int
6444da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6454da0d523SAndrew Gallatin {
646b824b7d8SAndrew Gallatin 
6474da0d523SAndrew Gallatin 
6484da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6494da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6504da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6514da0d523SAndrew Gallatin 		return EIO;
6524da0d523SAndrew Gallatin 	}
6534da0d523SAndrew Gallatin 
6544da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6554da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6564da0d523SAndrew Gallatin 	if (mxge_verbose)
6574da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6584da0d523SAndrew Gallatin 
659b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
660b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6614da0d523SAndrew Gallatin 
662b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
663b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6644da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6654da0d523SAndrew Gallatin 			      sc->fw_version);
6664da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6674da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6684da0d523SAndrew Gallatin 		return EINVAL;
6694da0d523SAndrew Gallatin 	}
6704da0d523SAndrew Gallatin 	return 0;
6714da0d523SAndrew Gallatin 
6724da0d523SAndrew Gallatin }
673b2fc195eSAndrew Gallatin 
674f9ae0280SAndrew Gallatin static void *
675f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
676f9ae0280SAndrew Gallatin {
677f9ae0280SAndrew Gallatin         void *ptr;
678f9ae0280SAndrew Gallatin 
679f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
680f9ae0280SAndrew Gallatin         return ptr;
681f9ae0280SAndrew Gallatin }
682f9ae0280SAndrew Gallatin 
683f9ae0280SAndrew Gallatin static void
684f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
685f9ae0280SAndrew Gallatin {
686f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
687f9ae0280SAndrew Gallatin }
688f9ae0280SAndrew Gallatin 
689f9ae0280SAndrew Gallatin 
690b2fc195eSAndrew Gallatin static int
6916d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
692b2fc195eSAndrew Gallatin {
693f9ae0280SAndrew Gallatin 	z_stream zs;
694f9ae0280SAndrew Gallatin 	char *inflate_buffer;
69533d54970SLuigi Rizzo 	const struct firmware *fw;
696b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
697b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
698b2fc195eSAndrew Gallatin 	int status;
6994da0d523SAndrew Gallatin 	unsigned int i;
7004da0d523SAndrew Gallatin 	char dummy;
701f9ae0280SAndrew Gallatin 	size_t fw_len;
702b2fc195eSAndrew Gallatin 
703b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
704b2fc195eSAndrew Gallatin 	if (fw == NULL) {
705b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
706b2fc195eSAndrew Gallatin 			      sc->fw_name);
707b2fc195eSAndrew Gallatin 		return ENOENT;
708b2fc195eSAndrew Gallatin 	}
709b2fc195eSAndrew Gallatin 
710f9ae0280SAndrew Gallatin 
711f9ae0280SAndrew Gallatin 
712f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
713f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
714f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
715f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
716f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
717f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
718b2fc195eSAndrew Gallatin 		status = EIO;
719b2fc195eSAndrew Gallatin 		goto abort_with_fw;
720b2fc195eSAndrew Gallatin 	}
721f9ae0280SAndrew Gallatin 
722f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
723f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
724f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
725f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
726f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
727f9ae0280SAndrew Gallatin 		goto abort_with_zs;
728f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
729f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
730f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
731f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
732f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
733f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
734f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
735f9ae0280SAndrew Gallatin 		status = EIO;
736f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
737f9ae0280SAndrew Gallatin 	}
738f9ae0280SAndrew Gallatin 
739f9ae0280SAndrew Gallatin 	/* check id */
740f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
741f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
742f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
743f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
744f9ae0280SAndrew Gallatin 		status = EIO;
745f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
746f9ae0280SAndrew Gallatin 	}
747f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
748b2fc195eSAndrew Gallatin 
7494da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7504da0d523SAndrew Gallatin 	if (status != 0)
751f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
752b2fc195eSAndrew Gallatin 
753b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
754f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7554da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
756f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
757f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
75873c7c83fSAndrew Gallatin 		wmb();
7594da0d523SAndrew Gallatin 		dummy = *sc->sram;
76073c7c83fSAndrew Gallatin 		wmb();
7614da0d523SAndrew Gallatin 	}
762b2fc195eSAndrew Gallatin 
763f9ae0280SAndrew Gallatin 	*limit = fw_len;
764b2fc195eSAndrew Gallatin 	status = 0;
765f9ae0280SAndrew Gallatin abort_with_buffer:
766f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
767f9ae0280SAndrew Gallatin abort_with_zs:
768f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
769b2fc195eSAndrew Gallatin abort_with_fw:
770b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
771b2fc195eSAndrew Gallatin 	return status;
772b2fc195eSAndrew Gallatin }
773b2fc195eSAndrew Gallatin 
774b2fc195eSAndrew Gallatin /*
775b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
776b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
777b2fc195eSAndrew Gallatin  */
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin static void
7806d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
781b2fc195eSAndrew Gallatin {
782b2fc195eSAndrew Gallatin 	char buf_bytes[72];
783b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
784b2fc195eSAndrew Gallatin 	volatile char *submit;
785b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
786b2fc195eSAndrew Gallatin 	int i;
787b2fc195eSAndrew Gallatin 
788b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
789b2fc195eSAndrew Gallatin 
790b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
791b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
792b2fc195eSAndrew Gallatin 	*confirm = 0;
79373c7c83fSAndrew Gallatin 	wmb();
794b2fc195eSAndrew Gallatin 
795b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
796b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
797b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
798b2fc195eSAndrew Gallatin 	*/
799b2fc195eSAndrew Gallatin 
8006d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8016d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
802b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
803b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
804b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8056d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8066d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
807b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
808b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
809b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
810b2fc195eSAndrew Gallatin 
811b2fc195eSAndrew Gallatin 
8120fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
813b2fc195eSAndrew Gallatin 
8146d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
81573c7c83fSAndrew Gallatin 	wmb();
816b2fc195eSAndrew Gallatin 	DELAY(1000);
81773c7c83fSAndrew Gallatin 	wmb();
818b2fc195eSAndrew Gallatin 	i = 0;
819b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
820b2fc195eSAndrew Gallatin 		DELAY(1000);
821b2fc195eSAndrew Gallatin 		i++;
822b2fc195eSAndrew Gallatin 	}
823b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
824b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
825b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
826b2fc195eSAndrew Gallatin 			      *confirm);
827b2fc195eSAndrew Gallatin 	}
828b2fc195eSAndrew Gallatin 	return;
829b2fc195eSAndrew Gallatin }
830b2fc195eSAndrew Gallatin 
831b2fc195eSAndrew Gallatin static int
8326d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
833b2fc195eSAndrew Gallatin {
834b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
835b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
836b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8370fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
838b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
839e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
840b2fc195eSAndrew Gallatin 
841b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
842b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
843b2fc195eSAndrew Gallatin 
844b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
845b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
846b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
847b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8486d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8496d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
850b2fc195eSAndrew Gallatin 
851b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
852b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
853a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
854b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
85573c7c83fSAndrew Gallatin 	wmb();
8566d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
857b2fc195eSAndrew Gallatin 
8585e7d8541SAndrew Gallatin 	/* wait up to 20ms */
859e0501fd0SAndrew Gallatin 	err = EAGAIN;
8605e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
861b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
862b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
86373c7c83fSAndrew Gallatin 		wmb();
864e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
865e0501fd0SAndrew Gallatin 		case 0:
866b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
867e0501fd0SAndrew Gallatin 			err = 0;
868e0501fd0SAndrew Gallatin 			break;
869e0501fd0SAndrew Gallatin 		case 0xffffffff:
870e0501fd0SAndrew Gallatin 			DELAY(1000);
871e0501fd0SAndrew Gallatin 			break;
872e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
873e0501fd0SAndrew Gallatin 			err = ENOSYS;
874e0501fd0SAndrew Gallatin 			break;
875e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
876e0501fd0SAndrew Gallatin 			err = E2BIG;
877e0501fd0SAndrew Gallatin 			break;
878c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
879c587e59fSAndrew Gallatin 			err = EBUSY;
880c587e59fSAndrew Gallatin 			break;
881e0501fd0SAndrew Gallatin 		default:
882b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8836d87a65dSAndrew Gallatin 				      "mxge: command %d "
884b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
885b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
886e0501fd0SAndrew Gallatin 			err = ENXIO;
887e0501fd0SAndrew Gallatin 			break;
888b2fc195eSAndrew Gallatin 		}
889e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
890e0501fd0SAndrew Gallatin 			break;
891b2fc195eSAndrew Gallatin 	}
892e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8936d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
894b2fc195eSAndrew Gallatin 			      "result = %d\n",
895b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
896e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
897e0501fd0SAndrew Gallatin 	return err;
898b2fc195eSAndrew Gallatin }
899b2fc195eSAndrew Gallatin 
9004da0d523SAndrew Gallatin static int
9014da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9024da0d523SAndrew Gallatin {
9034da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9044da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9054da0d523SAndrew Gallatin 	size_t hdr_offset;
9064da0d523SAndrew Gallatin 	int status;
9074da0d523SAndrew Gallatin 
9084da0d523SAndrew Gallatin 	/* find running firmware header */
9094da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9104da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9114da0d523SAndrew Gallatin 
9124da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9134da0d523SAndrew Gallatin 		device_printf(sc->dev,
9144da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9154da0d523SAndrew Gallatin 			      (int)hdr_offset);
9164da0d523SAndrew Gallatin 		return EIO;
9174da0d523SAndrew Gallatin 	}
9184da0d523SAndrew Gallatin 
9194da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9204da0d523SAndrew Gallatin 	 * validate firmware */
9214da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9224da0d523SAndrew Gallatin 	if (hdr == NULL) {
9234da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9244da0d523SAndrew Gallatin 		return ENOMEM;
9254da0d523SAndrew Gallatin 	}
9264da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9274da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9284da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9294da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9304da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
931b824b7d8SAndrew Gallatin 
932b824b7d8SAndrew Gallatin 	/*
933b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
934b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
935b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
936b824b7d8SAndrew Gallatin 	 */
937b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
938b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
939b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
940b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
941b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
942b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
943b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
944b824b7d8SAndrew Gallatin 	}
945b824b7d8SAndrew Gallatin 
9464da0d523SAndrew Gallatin 	return status;
9474da0d523SAndrew Gallatin }
9484da0d523SAndrew Gallatin 
949b2fc195eSAndrew Gallatin 
950b2fc195eSAndrew Gallatin static int
9511e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
952b2fc195eSAndrew Gallatin {
953b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
954b2fc195eSAndrew Gallatin 	volatile char *submit;
955b2fc195eSAndrew Gallatin 	char buf_bytes[72];
956b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
957b2fc195eSAndrew Gallatin 	int status, i;
958b2fc195eSAndrew Gallatin 
959b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
960b2fc195eSAndrew Gallatin 
961b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9626d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
963b2fc195eSAndrew Gallatin 	if (status) {
9641e413cf9SAndrew Gallatin 		if (!adopt)
9651e413cf9SAndrew Gallatin 			return status;
9664da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9674da0d523SAndrew Gallatin 		   it is new enough */
9684da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9694da0d523SAndrew Gallatin 		if (status) {
9704da0d523SAndrew Gallatin 			device_printf(sc->dev,
9714da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
972b2fc195eSAndrew Gallatin 			return status;
973b2fc195eSAndrew Gallatin 		}
9744da0d523SAndrew Gallatin 		device_printf(sc->dev,
9754da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9761e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9774da0d523SAndrew Gallatin 			device_printf(sc->dev,
9784da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9794da0d523SAndrew Gallatin 				 ".  For optimal\n");
9804da0d523SAndrew Gallatin 			device_printf(sc->dev,
9814da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9824da0d523SAndrew Gallatin 				 "firmware\n");
9834da0d523SAndrew Gallatin 		}
984d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9851e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
986d91b1b49SAndrew Gallatin 		return 0;
9874da0d523SAndrew Gallatin 	}
988b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
989b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
990b2fc195eSAndrew Gallatin 	*confirm = 0;
99173c7c83fSAndrew Gallatin 	wmb();
992b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
993b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
994b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
995b2fc195eSAndrew Gallatin 	*/
996b2fc195eSAndrew Gallatin 
9976d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9986d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
999b2fc195eSAndrew Gallatin 
1000b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1001b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1002b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1003b2fc195eSAndrew Gallatin 
1004b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1005b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1006b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1007b2fc195eSAndrew Gallatin 	*/
1008b2fc195eSAndrew Gallatin 					/* where the code starts*/
10096d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1010b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1011b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1012b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1013b2fc195eSAndrew Gallatin 
10140fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10156d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
101673c7c83fSAndrew Gallatin 	wmb();
1017b2fc195eSAndrew Gallatin 	DELAY(1000);
101873c7c83fSAndrew Gallatin 	wmb();
1019b2fc195eSAndrew Gallatin 	i = 0;
1020b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1021b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1022b2fc195eSAndrew Gallatin 		i++;
1023b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1024b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1025b2fc195eSAndrew Gallatin 	}
1026b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1027b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1028b2fc195eSAndrew Gallatin 			confirm, *confirm);
1029b2fc195eSAndrew Gallatin 
1030b2fc195eSAndrew Gallatin 		return ENXIO;
1031b2fc195eSAndrew Gallatin 	}
1032b2fc195eSAndrew Gallatin 	return 0;
1033b2fc195eSAndrew Gallatin }
1034b2fc195eSAndrew Gallatin 
1035b2fc195eSAndrew Gallatin static int
10366d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1037b2fc195eSAndrew Gallatin {
10386d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1039b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1040b2fc195eSAndrew Gallatin 	int status;
1041b2fc195eSAndrew Gallatin 
1042b2fc195eSAndrew Gallatin 
1043b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1044b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1045b2fc195eSAndrew Gallatin 
1046b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1047b2fc195eSAndrew Gallatin 
10485e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1049b2fc195eSAndrew Gallatin 	return status;
1050b2fc195eSAndrew Gallatin }
1051b2fc195eSAndrew Gallatin 
1052b2fc195eSAndrew Gallatin static int
10536d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1054b2fc195eSAndrew Gallatin {
10556d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1056b2fc195eSAndrew Gallatin 	int status;
1057b2fc195eSAndrew Gallatin 
1058b2fc195eSAndrew Gallatin 	if (pause)
10595e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1060b2fc195eSAndrew Gallatin 				       &cmd);
1061b2fc195eSAndrew Gallatin 	else
10625e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1063b2fc195eSAndrew Gallatin 				       &cmd);
1064b2fc195eSAndrew Gallatin 
1065b2fc195eSAndrew Gallatin 	if (status) {
1066b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1067b2fc195eSAndrew Gallatin 		return ENXIO;
1068b2fc195eSAndrew Gallatin 	}
1069b2fc195eSAndrew Gallatin 	sc->pause = pause;
1070b2fc195eSAndrew Gallatin 	return 0;
1071b2fc195eSAndrew Gallatin }
1072b2fc195eSAndrew Gallatin 
1073b2fc195eSAndrew Gallatin static void
10746d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1075b2fc195eSAndrew Gallatin {
10766d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1077b2fc195eSAndrew Gallatin 	int status;
1078b2fc195eSAndrew Gallatin 
10791e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10801e413cf9SAndrew Gallatin 		promisc = 1;
10811e413cf9SAndrew Gallatin 
1082b2fc195eSAndrew Gallatin 	if (promisc)
10835e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1084b2fc195eSAndrew Gallatin 				       &cmd);
1085b2fc195eSAndrew Gallatin 	else
10865e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1087b2fc195eSAndrew Gallatin 				       &cmd);
1088b2fc195eSAndrew Gallatin 
1089b2fc195eSAndrew Gallatin 	if (status) {
1090b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1091b2fc195eSAndrew Gallatin 	}
1092b2fc195eSAndrew Gallatin }
1093b2fc195eSAndrew Gallatin 
10940fa7f681SAndrew Gallatin static void
10950fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10960fa7f681SAndrew Gallatin {
10970fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10980fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10990fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
11000fa7f681SAndrew Gallatin 	int err;
11010fa7f681SAndrew Gallatin 
11020fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11030fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11040fa7f681SAndrew Gallatin 		return;
11050fa7f681SAndrew Gallatin 
11060fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11070fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11080fa7f681SAndrew Gallatin 	if (err != 0) {
11090fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11100fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11110fa7f681SAndrew Gallatin 		return;
11120fa7f681SAndrew Gallatin 	}
11130fa7f681SAndrew Gallatin 
1114b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1115b824b7d8SAndrew Gallatin 		return;
11160fa7f681SAndrew Gallatin 
11170fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11180fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11190fa7f681SAndrew Gallatin 		return;
11200fa7f681SAndrew Gallatin 
11210fa7f681SAndrew Gallatin 	/* Flush all the filters */
11220fa7f681SAndrew Gallatin 
11230fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11240fa7f681SAndrew Gallatin 	if (err != 0) {
11250fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11260fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11270fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11280fa7f681SAndrew Gallatin 		return;
11290fa7f681SAndrew Gallatin 	}
11300fa7f681SAndrew Gallatin 
11310fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11320fa7f681SAndrew Gallatin 
1133eb956cd0SRobert Watson 	if_maddr_rlock(ifp);
11340fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11350fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11360fa7f681SAndrew Gallatin 			continue;
11370fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11380fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11390fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11400fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11410fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11420fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11430fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11440fa7f681SAndrew Gallatin 		if (err != 0) {
11450fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11460fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11470fa7f681SAndrew Gallatin 			       "%d\t", err);
11480fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
1149eb956cd0SRobert Watson 			if_maddr_runlock(ifp);
11500fa7f681SAndrew Gallatin 			return;
11510fa7f681SAndrew Gallatin 		}
11520fa7f681SAndrew Gallatin 	}
1153eb956cd0SRobert Watson 	if_maddr_runlock(ifp);
11540fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11550fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11560fa7f681SAndrew Gallatin 	if (err != 0) {
11570fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11580fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11590fa7f681SAndrew Gallatin 	}
11600fa7f681SAndrew Gallatin }
11610fa7f681SAndrew Gallatin 
1162b2fc195eSAndrew Gallatin static int
1163053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1164053e637fSAndrew Gallatin {
1165053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1166053e637fSAndrew Gallatin 	int status;
1167053e637fSAndrew Gallatin 
1168c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1169c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1170053e637fSAndrew Gallatin 
1171053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1172053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1173053e637fSAndrew Gallatin 	cmd.data0 = 0;
1174053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1175053e637fSAndrew Gallatin 			       &cmd);
1176053e637fSAndrew Gallatin 	if (status == 0)
1177c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1178053e637fSAndrew Gallatin 
1179053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1180053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1181053e637fSAndrew Gallatin }
1182053e637fSAndrew Gallatin 
1183053e637fSAndrew Gallatin static int
1184adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1185b2fc195eSAndrew Gallatin {
11861e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11871e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11881e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11896d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11901e413cf9SAndrew Gallatin 	int slice, status;
1191b2fc195eSAndrew Gallatin 
1192b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1193b2fc195eSAndrew Gallatin 	   is alive */
1194b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11955e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1196b2fc195eSAndrew Gallatin 	if (status != 0) {
1197b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1198b2fc195eSAndrew Gallatin 		return ENXIO;
1199b2fc195eSAndrew Gallatin 	}
1200b2fc195eSAndrew Gallatin 
1201091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1202091feecdSAndrew Gallatin 
12031e413cf9SAndrew Gallatin 
12041e413cf9SAndrew Gallatin 	/* set the intrq size */
12051e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12061e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12071e413cf9SAndrew Gallatin 
12081e413cf9SAndrew Gallatin 	/*
12091e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12101e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12111e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12121e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12131e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12141e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12151e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12161e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12171e413cf9SAndrew Gallatin 	 */
12181e413cf9SAndrew Gallatin 
12191e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12201e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12211e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12221e413cf9SAndrew Gallatin 					   &cmd);
12231e413cf9SAndrew Gallatin 		if (status != 0) {
12241e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12251e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12261e413cf9SAndrew Gallatin 			return status;
12271e413cf9SAndrew Gallatin 		}
12281e413cf9SAndrew Gallatin 		/*
12291e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12301e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12311e413cf9SAndrew Gallatin 		 */
12321e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12331e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1234c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1235c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1236c6cb3e3fSAndrew Gallatin #endif
12371e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12381e413cf9SAndrew Gallatin 					   &cmd);
12391e413cf9SAndrew Gallatin 		if (status != 0) {
12401e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12411e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12421e413cf9SAndrew Gallatin 			return status;
12431e413cf9SAndrew Gallatin 		}
12441e413cf9SAndrew Gallatin 	}
12451e413cf9SAndrew Gallatin 
12461e413cf9SAndrew Gallatin 
1247adae7080SAndrew Gallatin 	if (interrupts_setup) {
1248b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12491e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12501e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12511e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12521e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12531e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12541e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12551e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12561e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12571e413cf9SAndrew Gallatin 						&cmd);
12581e413cf9SAndrew Gallatin 		}
1259adae7080SAndrew Gallatin 	}
1260b2fc195eSAndrew Gallatin 
12616d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12625e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12635e7d8541SAndrew Gallatin 
12645e7d8541SAndrew Gallatin 
12655e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12665e7d8541SAndrew Gallatin 
12675e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12681e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12695e7d8541SAndrew Gallatin 
12705e7d8541SAndrew Gallatin 
12715e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12726d87a65dSAndrew Gallatin 				&cmd);
12735e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1274b2fc195eSAndrew Gallatin 	if (status != 0) {
1275b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1276b2fc195eSAndrew Gallatin 		return status;
1277b2fc195eSAndrew Gallatin 	}
1278b2fc195eSAndrew Gallatin 
12795e7d8541SAndrew Gallatin 
12805e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12815e7d8541SAndrew Gallatin 
12825e7d8541SAndrew Gallatin 
12835e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12848fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12855e7d8541SAndrew Gallatin 
12861e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12871e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12881e413cf9SAndrew Gallatin 
12891e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1290b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12911e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12921e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12931e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12941e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12951e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1296c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1297c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1298c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12991e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
13001e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13011e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13021e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13031e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
13041e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
13051e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
13061e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
13071e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
13081e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
13091e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
13101e413cf9SAndrew Gallatin 		}
13111e413cf9SAndrew Gallatin 	}
1312b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13136d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1314bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13156d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13160fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1317b2fc195eSAndrew Gallatin 	return status;
1318b2fc195eSAndrew Gallatin }
1319b2fc195eSAndrew Gallatin 
1320b2fc195eSAndrew Gallatin static int
13216d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1322b2fc195eSAndrew Gallatin {
13236d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1324b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1325b2fc195eSAndrew Gallatin         int err;
1326b2fc195eSAndrew Gallatin 
1327b2fc195eSAndrew Gallatin         sc = arg1;
1328b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1329b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1330b2fc195eSAndrew Gallatin         if (err != 0) {
1331b2fc195eSAndrew Gallatin                 return err;
1332b2fc195eSAndrew Gallatin         }
1333b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1334b2fc195eSAndrew Gallatin                 return 0;
1335b2fc195eSAndrew Gallatin 
1336b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1337b2fc195eSAndrew Gallatin                 return EINVAL;
1338b2fc195eSAndrew Gallatin 
1339a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13405e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1341b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13425e7d8541SAndrew Gallatin 
1343a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1344b2fc195eSAndrew Gallatin         return err;
1345b2fc195eSAndrew Gallatin }
1346b2fc195eSAndrew Gallatin 
1347b2fc195eSAndrew Gallatin static int
13486d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1349b2fc195eSAndrew Gallatin {
13506d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1351b2fc195eSAndrew Gallatin         unsigned int enabled;
1352b2fc195eSAndrew Gallatin         int err;
1353b2fc195eSAndrew Gallatin 
1354b2fc195eSAndrew Gallatin         sc = arg1;
1355b2fc195eSAndrew Gallatin         enabled = sc->pause;
1356b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1357b2fc195eSAndrew Gallatin         if (err != 0) {
1358b2fc195eSAndrew Gallatin                 return err;
1359b2fc195eSAndrew Gallatin         }
1360b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1361b2fc195eSAndrew Gallatin                 return 0;
1362b2fc195eSAndrew Gallatin 
1363a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13646d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1365a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1366b2fc195eSAndrew Gallatin         return err;
1367b2fc195eSAndrew Gallatin }
1368b2fc195eSAndrew Gallatin 
1369b2fc195eSAndrew Gallatin static int
1370f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1371f04b33f8SAndrew Gallatin {
1372f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1373c587e59fSAndrew Gallatin 	int err = 0;
1374f04b33f8SAndrew Gallatin 
1375f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1376f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1377f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1378f04b33f8SAndrew Gallatin 	else
1379f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1380f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1381c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1382f04b33f8SAndrew Gallatin 		mxge_close(sc);
1383f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1384c587e59fSAndrew Gallatin 	}
1385f04b33f8SAndrew Gallatin 	return err;
1386f04b33f8SAndrew Gallatin }
1387f04b33f8SAndrew Gallatin 
1388f04b33f8SAndrew Gallatin static int
1389276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1390276edd10SAndrew Gallatin {
1391276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1392276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1393276edd10SAndrew Gallatin 	int err;
1394276edd10SAndrew Gallatin 
1395276edd10SAndrew Gallatin 	sc = arg1;
1396276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1397276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1398276edd10SAndrew Gallatin 	if (err != 0)
1399276edd10SAndrew Gallatin 		return err;
1400276edd10SAndrew Gallatin 
1401276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1402276edd10SAndrew Gallatin 		return 0;
1403276edd10SAndrew Gallatin 
1404276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1405276edd10SAndrew Gallatin 		return EINVAL;
1406276edd10SAndrew Gallatin 
1407276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1408f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1409276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1410276edd10SAndrew Gallatin 	return err;
1411276edd10SAndrew Gallatin }
1412276edd10SAndrew Gallatin 
1413276edd10SAndrew Gallatin static int
14146d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1415b2fc195eSAndrew Gallatin {
1416b2fc195eSAndrew Gallatin         int err;
1417b2fc195eSAndrew Gallatin 
1418b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1419b2fc195eSAndrew Gallatin                 return EFAULT;
1420b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1421b2fc195eSAndrew Gallatin         arg1 = NULL;
1422b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1423b2fc195eSAndrew Gallatin 
1424b2fc195eSAndrew Gallatin         return err;
1425b2fc195eSAndrew Gallatin }
1426b2fc195eSAndrew Gallatin 
1427b2fc195eSAndrew Gallatin static void
14281e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14291e413cf9SAndrew Gallatin {
14301e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14311e413cf9SAndrew Gallatin 	int slice;
14321e413cf9SAndrew Gallatin 
14331e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14341e413cf9SAndrew Gallatin 		return;
14351e413cf9SAndrew Gallatin 
14361e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14371e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14381e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14391e413cf9SAndrew Gallatin 			continue;
14401e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14411e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14421e413cf9SAndrew Gallatin 	}
14431e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14441e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14451e413cf9SAndrew Gallatin }
14461e413cf9SAndrew Gallatin 
14471e413cf9SAndrew Gallatin static void
14486d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1449b2fc195eSAndrew Gallatin {
1450b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1451b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14525e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14531e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14541e413cf9SAndrew Gallatin 	int slice;
14551e413cf9SAndrew Gallatin 	char slice_num[8];
1456b2fc195eSAndrew Gallatin 
1457b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1458b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14591e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1460b2fc195eSAndrew Gallatin 
14615e7d8541SAndrew Gallatin 	/* random information */
14625e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14635e7d8541SAndrew Gallatin 		       "firmware_version",
14645e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
14655e7d8541SAndrew Gallatin 		       0, "firmware version");
14665e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14675e7d8541SAndrew Gallatin 		       "serial_number",
14685e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
14695e7d8541SAndrew Gallatin 		       0, "serial number");
14705e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14715e7d8541SAndrew Gallatin 		       "product_code",
14725e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
14735e7d8541SAndrew Gallatin 		       0, "product_code");
14745e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1475d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1476d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1477d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1478d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14795e7d8541SAndrew Gallatin 		       "tx_boundary",
14801e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14815e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14825e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1483091feecdSAndrew Gallatin 		       "write_combine",
1484091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1485091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1486091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14875e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14885e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14895e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14905e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14915e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14925e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14935e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14945e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14955e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14965e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14975e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
14985e7d8541SAndrew Gallatin 
14995e7d8541SAndrew Gallatin 
15005e7d8541SAndrew Gallatin 	/* performance related tunables */
1501b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1502b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1503b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15046d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1505b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1506b2fc195eSAndrew Gallatin 
1507b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1508b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1509b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15106d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1511b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1512b2fc195eSAndrew Gallatin 
1513b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15145e7d8541SAndrew Gallatin 		       "deassert_wait",
15155e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15165e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1517b2fc195eSAndrew Gallatin 
1518b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1519b2fc195eSAndrew Gallatin 	   Need to swap it */
1520b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1521b2fc195eSAndrew Gallatin 			"link_up",
1522b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15236d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1524b2fc195eSAndrew Gallatin 			"I", "link up");
1525b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1526b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1527b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15286d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1529b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1530b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1531adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1532adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1533adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15346d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1535adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1536adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1537adae7080SAndrew Gallatin 			"dropped_bad_phy",
1538adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1539adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1540adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1541adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1542b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1543b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1544b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1545b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15466d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1547b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1548b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1549adae7080SAndrew Gallatin 			"dropped_link_overflow",
1550adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1551adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1552adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1553adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15540fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15550fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15560fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15570fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15580fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15590fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1560adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1561adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15626d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1563adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1564b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1565b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1566b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1567b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15686d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1569b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1570b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1571adae7080SAndrew Gallatin 			"dropped_overrun",
1572adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15736d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1574adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1575adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1576adae7080SAndrew Gallatin 			"dropped_pause",
1577adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1578adae7080SAndrew Gallatin 			&fw->dropped_pause,
1579adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1580adae7080SAndrew Gallatin 			"I", "dropped_pause");
1581adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1582adae7080SAndrew Gallatin 			"dropped_runt",
1583adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1584adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1585adae7080SAndrew Gallatin 			"I", "dropped_runt");
1586b2fc195eSAndrew Gallatin 
1587a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1588a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1589a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1590a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1591a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1592a0394e33SAndrew Gallatin 
15935e7d8541SAndrew Gallatin 	/* verbose printing? */
1594b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15955e7d8541SAndrew Gallatin 		       "verbose",
15965e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15975e7d8541SAndrew Gallatin 		       0, "verbose printing");
1598b2fc195eSAndrew Gallatin 
1599053e637fSAndrew Gallatin 	/* lro */
1600276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1601276edd10SAndrew Gallatin 			"lro_cnt",
1602276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1603276edd10SAndrew Gallatin 			0, mxge_change_lro,
1604276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1605053e637fSAndrew Gallatin 
16061e413cf9SAndrew Gallatin 
16071e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16081e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16091e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16101e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16111e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16121e413cf9SAndrew Gallatin 
16131e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16141e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16151e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16161e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16171e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16181e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16191e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16201e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16211e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16221e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1623053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16241e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16251e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16261e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16271e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16281e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16291e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16301e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16311e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16321e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1633053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1634053e637fSAndrew Gallatin 
1635053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16361e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16371e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16381e413cf9SAndrew Gallatin 			       "queues");
1639053e637fSAndrew Gallatin 
1640c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16411e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16421e413cf9SAndrew Gallatin 		if (slice > 0)
16431e413cf9SAndrew Gallatin 			continue;
1644c6cb3e3fSAndrew Gallatin #endif
1645c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1646c6cb3e3fSAndrew Gallatin 			       "tx_req",
1647c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1648c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16491e413cf9SAndrew Gallatin 
16501e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16511e413cf9SAndrew Gallatin 			       "tx_done",
16521e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16531e413cf9SAndrew Gallatin 			       0, "tx_done");
16541e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16551e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16561e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16571e413cf9SAndrew Gallatin 			       0, "tx_done");
16581e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16591e413cf9SAndrew Gallatin 			       "tx_stall",
16601e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16611e413cf9SAndrew Gallatin 			       0, "tx_stall");
16621e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16631e413cf9SAndrew Gallatin 			       "tx_wake",
16641e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16651e413cf9SAndrew Gallatin 			       0, "tx_wake");
16661e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16671e413cf9SAndrew Gallatin 			       "tx_defrag",
16681e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16691e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1670c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1671c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1672c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1673c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1674c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1675c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1676c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1677c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1678c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1679c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1680c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1681c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16821e413cf9SAndrew Gallatin 	}
1683b2fc195eSAndrew Gallatin }
1684b2fc195eSAndrew Gallatin 
1685b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1686b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1687b2fc195eSAndrew Gallatin 
1688b2fc195eSAndrew Gallatin static inline void
16891e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1690b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1691b2fc195eSAndrew Gallatin {
1692b2fc195eSAndrew Gallatin         int idx, starting_slot;
1693b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1694b2fc195eSAndrew Gallatin         while (cnt > 1) {
1695b2fc195eSAndrew Gallatin                 cnt--;
1696b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
16976d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1698b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
169973c7c83fSAndrew Gallatin                 wmb();
1700b2fc195eSAndrew Gallatin         }
1701b2fc195eSAndrew Gallatin }
1702b2fc195eSAndrew Gallatin 
1703b2fc195eSAndrew Gallatin /*
1704b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1705b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1706b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1707b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1708b2fc195eSAndrew Gallatin  */
1709b2fc195eSAndrew Gallatin 
1710b2fc195eSAndrew Gallatin static inline void
17111e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1712b2fc195eSAndrew Gallatin                   int cnt)
1713b2fc195eSAndrew Gallatin {
1714b2fc195eSAndrew Gallatin         int idx, i;
1715b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1716b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1717b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1718b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17195e7d8541SAndrew Gallatin 	uint8_t last_flags;
1720b2fc195eSAndrew Gallatin 
1721b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1722b2fc195eSAndrew Gallatin 
17235e7d8541SAndrew Gallatin 	last_flags = src->flags;
17245e7d8541SAndrew Gallatin 	src->flags = 0;
172573c7c83fSAndrew Gallatin         wmb();
1726b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1727b2fc195eSAndrew Gallatin         srcp = src;
1728b2fc195eSAndrew Gallatin 
1729b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1730b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17316d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
173273c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1733b2fc195eSAndrew Gallatin                         srcp += 2;
1734b2fc195eSAndrew Gallatin                         dstp += 2;
1735b2fc195eSAndrew Gallatin                 }
1736b2fc195eSAndrew Gallatin         } else {
1737b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1738b2fc195eSAndrew Gallatin                    that it is submitted below */
17396d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1740b2fc195eSAndrew Gallatin                 i = 0;
1741b2fc195eSAndrew Gallatin         }
1742b2fc195eSAndrew Gallatin         if (i < cnt) {
1743b2fc195eSAndrew Gallatin                 /* submit the first request */
17446d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
174573c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1746b2fc195eSAndrew Gallatin         }
1747b2fc195eSAndrew Gallatin 
1748b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17495e7d8541SAndrew Gallatin         src->flags = last_flags;
1750b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1751b2fc195eSAndrew Gallatin         src_ints+=3;
1752b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1753b2fc195eSAndrew Gallatin         dst_ints+=3;
1754b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1755b2fc195eSAndrew Gallatin         tx->req += cnt;
175673c7c83fSAndrew Gallatin         wmb();
1757b2fc195eSAndrew Gallatin }
1758b2fc195eSAndrew Gallatin 
175937d89b0cSAndrew Gallatin #if IFCAP_TSO4
176037d89b0cSAndrew Gallatin 
1761b2fc195eSAndrew Gallatin static void
17621e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17631e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1764aed8e389SAndrew Gallatin {
17651e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1766aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1767aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1768aed8e389SAndrew Gallatin 	struct ip *ip;
1769aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1770aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1771aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1772aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1773aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1774aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1775aed8e389SAndrew Gallatin 	static int once;
1776aed8e389SAndrew Gallatin 
1777aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1778aed8e389SAndrew Gallatin 
1779aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1780aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1781aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1782aed8e389SAndrew Gallatin 	 */
1783aed8e389SAndrew Gallatin 
1784aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1785aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1786aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1787c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1788c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
17891e413cf9SAndrew Gallatin 			   ss->scratch);
17901e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1791aed8e389SAndrew Gallatin 	} else {
1792c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1793aed8e389SAndrew Gallatin 	}
1794c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1795aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1796c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
17971e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1798c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1799aed8e389SAndrew Gallatin 	}
1800aed8e389SAndrew Gallatin 
1801aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1802c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1803aed8e389SAndrew Gallatin 
1804aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1805c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1806aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1807aed8e389SAndrew Gallatin 
1808aed8e389SAndrew Gallatin 
1809aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1810aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1811aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1812aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1813aed8e389SAndrew Gallatin 
18141e413cf9SAndrew Gallatin 	tx = &ss->tx;
1815aed8e389SAndrew Gallatin 	req = tx->req_list;
1816aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1817aed8e389SAndrew Gallatin 	cnt = 0;
1818aed8e389SAndrew Gallatin 	rdma_count = 0;
1819aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1820aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1821aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1822aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1823aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1824aed8e389SAndrew Gallatin 	 *
1825aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1826aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1827aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1828aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1829aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1830aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1831aed8e389SAndrew Gallatin 	 *
1832aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1833aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1834aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1835aed8e389SAndrew Gallatin 	 */
1836aed8e389SAndrew Gallatin 
1837aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1838aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1839aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1840aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1841e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1842aed8e389SAndrew Gallatin 
1843aed8e389SAndrew Gallatin 		while (len) {
1844aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1845e39a0a37SAndrew Gallatin 			seglen = len;
1846aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1847aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1848aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1849aed8e389SAndrew Gallatin 				/* payload */
1850aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1851aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1852aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1853aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1854aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1855aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1856aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1857aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1858aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1859aed8e389SAndrew Gallatin 				/* header ends */
1860aed8e389SAndrew Gallatin 				rdma_count = -1;
1861aed8e389SAndrew Gallatin 				cum_len_next = 0;
1862aed8e389SAndrew Gallatin 				seglen = -cum_len;
1863aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1864aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1865aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1866aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1867aed8e389SAndrew Gallatin 			    }
1868aed8e389SAndrew Gallatin 
1869aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1870aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1871aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1872aed8e389SAndrew Gallatin 			req->pad = 0;
1873aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1874aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1875aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1876aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1877aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1878aed8e389SAndrew Gallatin 			low += seglen;
1879aed8e389SAndrew Gallatin 			len -= seglen;
1880aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1881aed8e389SAndrew Gallatin 			flags = flags_next;
1882aed8e389SAndrew Gallatin 			req++;
1883aed8e389SAndrew Gallatin 			cnt++;
1884aed8e389SAndrew Gallatin 			rdma_count++;
1885aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1886aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1887aed8e389SAndrew Gallatin 			else
1888aed8e389SAndrew Gallatin 				cksum_offset = 0;
1889adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1890aed8e389SAndrew Gallatin 				goto drop;
1891aed8e389SAndrew Gallatin 		}
1892aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1893aed8e389SAndrew Gallatin 		seg++;
1894aed8e389SAndrew Gallatin 	}
1895aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1896aed8e389SAndrew Gallatin 
1897aed8e389SAndrew Gallatin 	do {
1898aed8e389SAndrew Gallatin 		req--;
1899aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1900aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1901aed8e389SAndrew Gallatin 
1902aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1903aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1904c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1905c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1906c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1907c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1908c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1909c6cb3e3fSAndrew Gallatin 		tx->activate++;
1910c6cb3e3fSAndrew Gallatin 		wmb();
1911c6cb3e3fSAndrew Gallatin 	}
1912c6cb3e3fSAndrew Gallatin #endif
1913aed8e389SAndrew Gallatin 	return;
1914aed8e389SAndrew Gallatin 
1915aed8e389SAndrew Gallatin drop:
1916e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1917aed8e389SAndrew Gallatin 	m_freem(m);
1918c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1919aed8e389SAndrew Gallatin 	if (!once) {
1920adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1921adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1922adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1923aed8e389SAndrew Gallatin 		once = 1;
1924aed8e389SAndrew Gallatin 	}
1925aed8e389SAndrew Gallatin 	return;
1926aed8e389SAndrew Gallatin 
1927aed8e389SAndrew Gallatin }
1928aed8e389SAndrew Gallatin 
192937d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
193037d89b0cSAndrew Gallatin 
193137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1932c792928fSAndrew Gallatin /*
1933c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1934c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1935c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1936c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1937c792928fSAndrew Gallatin  */
1938c792928fSAndrew Gallatin static struct mbuf *
1939c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1940c792928fSAndrew Gallatin {
1941c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1942c792928fSAndrew Gallatin 
1943c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1944c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1945c792928fSAndrew Gallatin 		return NULL;
1946c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1947c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1948c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1949c792928fSAndrew Gallatin 			return NULL;
1950c792928fSAndrew Gallatin 	}
1951c792928fSAndrew Gallatin 	/*
1952c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1953c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1954c792928fSAndrew Gallatin 	 */
1955c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1956c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1957c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1958c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1959c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1960c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1961c792928fSAndrew Gallatin 	return m;
1962c792928fSAndrew Gallatin }
196337d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
1964c792928fSAndrew Gallatin 
1965aed8e389SAndrew Gallatin static void
19661e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
1967b2fc195eSAndrew Gallatin {
19681e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
1969b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1970b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1971b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1972b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19731e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1974b2fc195eSAndrew Gallatin 	struct ip *ip;
1975c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1976aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1977aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1978b2fc195eSAndrew Gallatin 
1979b2fc195eSAndrew Gallatin 
19801e413cf9SAndrew Gallatin 	sc = ss->sc;
1981b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19821e413cf9SAndrew Gallatin 	tx = &ss->tx;
1983b2fc195eSAndrew Gallatin 
1984c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
198537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1986c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1987c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1988c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1989c792928fSAndrew Gallatin 			goto drop;
1990c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1991c792928fSAndrew Gallatin 	}
199237d89b0cSAndrew Gallatin #endif
1993b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1994b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1995b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1996aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1997b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1998adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1999b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2000b2fc195eSAndrew Gallatin 		   to defrag */
2001b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2002b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2003b2fc195eSAndrew Gallatin 			goto drop;
2004b2fc195eSAndrew Gallatin 		}
20051e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2006b2fc195eSAndrew Gallatin 		m = m_tmp;
2007b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2008b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2009aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2010b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2011b2fc195eSAndrew Gallatin 	}
2012adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2013aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2014aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2015b2fc195eSAndrew Gallatin 		goto drop;
2016b2fc195eSAndrew Gallatin 	}
2017b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2018b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20195e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2020b2fc195eSAndrew Gallatin 
202137d89b0cSAndrew Gallatin #if IFCAP_TSO4
2022aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2023aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20241e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
2025aed8e389SAndrew Gallatin 		return;
2026aed8e389SAndrew Gallatin 	}
202737d89b0cSAndrew Gallatin #endif
2028aed8e389SAndrew Gallatin 
2029b2fc195eSAndrew Gallatin 	req = tx->req_list;
2030b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20315e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20325e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2033b2fc195eSAndrew Gallatin 
2034b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2035b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2036aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2037aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2038c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2039c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20401e413cf9SAndrew Gallatin 				   ss->scratch);
20411e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2042aed8e389SAndrew Gallatin 		} else {
2043c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2044aed8e389SAndrew Gallatin 		}
2045c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2046b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20475e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2048b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20495e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2050aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2051aed8e389SAndrew Gallatin 	} else {
2052aed8e389SAndrew Gallatin 		odd_flag = 0;
2053b2fc195eSAndrew Gallatin 	}
20545e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20555e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2056b2fc195eSAndrew Gallatin 
2057b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2058b2fc195eSAndrew Gallatin 	cum_len = 0;
2059aed8e389SAndrew Gallatin 	seg = tx->seg_list;
20605e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2061b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2062b2fc195eSAndrew Gallatin 		req->addr_low =
20636d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2064b2fc195eSAndrew Gallatin 		req->addr_high =
20656d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2066b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2067b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2068b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2069b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2070b2fc195eSAndrew Gallatin 		else
2071b2fc195eSAndrew Gallatin 			cksum_offset = 0;
20725e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20735e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20745e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2075aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2076b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2077b2fc195eSAndrew Gallatin 		seg++;
2078b2fc195eSAndrew Gallatin 		req++;
2079b2fc195eSAndrew Gallatin 		req->flags = 0;
2080b2fc195eSAndrew Gallatin 	}
2081b2fc195eSAndrew Gallatin 	req--;
2082b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2083b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2084b2fc195eSAndrew Gallatin 		req++;
2085b2fc195eSAndrew Gallatin 		req->addr_low =
20866d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2087b2fc195eSAndrew Gallatin 		req->addr_high =
20886d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2089b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
20905e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
20915e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20925e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20935e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2094aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2095b2fc195eSAndrew Gallatin 		cnt++;
2096b2fc195eSAndrew Gallatin 	}
20975e7d8541SAndrew Gallatin 
20985e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
20995e7d8541SAndrew Gallatin #if 0
21005e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21015e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21025e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21035e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21045e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21055e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21065e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21075e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21085e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21095e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21105e7d8541SAndrew Gallatin 	}
21115e7d8541SAndrew Gallatin 	printf("--------------\n");
21125e7d8541SAndrew Gallatin #endif
21135e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21146d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2115c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2116c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2117c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2118c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2119c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2120c6cb3e3fSAndrew Gallatin 		tx->activate++;
2121c6cb3e3fSAndrew Gallatin 		wmb();
2122c6cb3e3fSAndrew Gallatin 	}
2123c6cb3e3fSAndrew Gallatin #endif
2124b2fc195eSAndrew Gallatin 	return;
2125b2fc195eSAndrew Gallatin 
2126b2fc195eSAndrew Gallatin drop:
2127b2fc195eSAndrew Gallatin 	m_freem(m);
2128c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2129b2fc195eSAndrew Gallatin 	return;
2130b2fc195eSAndrew Gallatin }
2131b2fc195eSAndrew Gallatin 
2132c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2133c6cb3e3fSAndrew Gallatin static void
2134c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2135c6cb3e3fSAndrew Gallatin {
2136c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2137c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2138c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2139c6cb3e3fSAndrew Gallatin 	int slice;
2140b2fc195eSAndrew Gallatin 
2141c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2142c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2143c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2144c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2145c6cb3e3fSAndrew Gallatin 			m_freem(m);
2146c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2147c6cb3e3fSAndrew Gallatin 	}
2148c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2149c6cb3e3fSAndrew Gallatin }
21506d914a32SAndrew Gallatin 
2151c6cb3e3fSAndrew Gallatin static inline void
2152c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2153c6cb3e3fSAndrew Gallatin {
2154c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2155c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2156c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2157c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2158c6cb3e3fSAndrew Gallatin 
2159c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2160c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2161c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2162c6cb3e3fSAndrew Gallatin 
2163c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2164c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2165c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2166c6cb3e3fSAndrew Gallatin 			return;
2167c6cb3e3fSAndrew Gallatin 		}
2168c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2169c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2170c6cb3e3fSAndrew Gallatin 
2171c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2172c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2173c6cb3e3fSAndrew Gallatin 	}
2174c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2175c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2176c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2177c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2178c6cb3e3fSAndrew Gallatin 		tx->stall++;
2179c6cb3e3fSAndrew Gallatin 	}
2180c6cb3e3fSAndrew Gallatin }
2181c6cb3e3fSAndrew Gallatin 
2182c6cb3e3fSAndrew Gallatin static int
2183c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2184c6cb3e3fSAndrew Gallatin {
2185c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2186c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2187c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2188c6cb3e3fSAndrew Gallatin 	int err;
2189c6cb3e3fSAndrew Gallatin 
2190c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2191c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2192c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2193c6cb3e3fSAndrew Gallatin 
2194c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2195c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2196c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2197c6cb3e3fSAndrew Gallatin 		return (err);
2198c6cb3e3fSAndrew Gallatin 	}
2199c6cb3e3fSAndrew Gallatin 
2200c6cb3e3fSAndrew Gallatin 	if (drbr_empty(ifp, tx->br) &&
2201c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2202c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2203c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2204c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2205c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2206c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2207c6cb3e3fSAndrew Gallatin 		return (err);
2208c6cb3e3fSAndrew Gallatin 	}
2209c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2210c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2211c6cb3e3fSAndrew Gallatin 	return (0);
2212c6cb3e3fSAndrew Gallatin }
2213c6cb3e3fSAndrew Gallatin 
2214c6cb3e3fSAndrew Gallatin static int
2215c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2216c6cb3e3fSAndrew Gallatin {
2217c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2218c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2219c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2220c6cb3e3fSAndrew Gallatin 	int err = 0;
2221c6cb3e3fSAndrew Gallatin 	int slice;
2222c6cb3e3fSAndrew Gallatin 
2223c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2224c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2225c6cb3e3fSAndrew Gallatin 
2226c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2227c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2228c6cb3e3fSAndrew Gallatin 
2229c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2230c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2231c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2232c6cb3e3fSAndrew Gallatin 	} else {
2233c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2234c6cb3e3fSAndrew Gallatin 	}
2235c6cb3e3fSAndrew Gallatin 
2236c6cb3e3fSAndrew Gallatin 	return (err);
2237c6cb3e3fSAndrew Gallatin }
2238c6cb3e3fSAndrew Gallatin 
2239c6cb3e3fSAndrew Gallatin #else
22406d914a32SAndrew Gallatin 
22416d914a32SAndrew Gallatin static inline void
22421e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2243b2fc195eSAndrew Gallatin {
22441e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2245b2fc195eSAndrew Gallatin 	struct mbuf *m;
2246b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22471e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2248b2fc195eSAndrew Gallatin 
22491e413cf9SAndrew Gallatin 	sc = ss->sc;
2250b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
22511e413cf9SAndrew Gallatin 	tx = &ss->tx;
2252adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
22536d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
22546d914a32SAndrew Gallatin 		if (m == NULL) {
22556d914a32SAndrew Gallatin 			return;
22566d914a32SAndrew Gallatin 		}
2257b2fc195eSAndrew Gallatin 		/* let BPF see it */
2258b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2259b2fc195eSAndrew Gallatin 
2260b2fc195eSAndrew Gallatin 		/* give it to the nic */
22611e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
22626d914a32SAndrew Gallatin 	}
22636d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2264a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2265b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2266adae7080SAndrew Gallatin 		tx->stall++;
2267a82c2581SAndrew Gallatin 	}
2268b2fc195eSAndrew Gallatin }
2269c6cb3e3fSAndrew Gallatin #endif
2270b2fc195eSAndrew Gallatin static void
22716d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2272b2fc195eSAndrew Gallatin {
22736d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
22741e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2275b2fc195eSAndrew Gallatin 
22761e413cf9SAndrew Gallatin 	/* only use the first slice for now */
22771e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
22781e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
22791e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
22801e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2281b2fc195eSAndrew Gallatin }
2282b2fc195eSAndrew Gallatin 
22835e7d8541SAndrew Gallatin /*
22845e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
22855e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
22865e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
22875e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
22885e7d8541SAndrew Gallatin  * in a burst
22895e7d8541SAndrew Gallatin  */
22905e7d8541SAndrew Gallatin static inline void
22915e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
22925e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
22935e7d8541SAndrew Gallatin {
22945e7d8541SAndrew Gallatin 	uint32_t low;
22955e7d8541SAndrew Gallatin 
22965e7d8541SAndrew Gallatin 	low = src->addr_low;
22975e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2298a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
229973c7c83fSAndrew Gallatin 	wmb();
2300a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
230173c7c83fSAndrew Gallatin 	wmb();
230240385a5fSAndrew Gallatin 	src->addr_low = low;
23035e7d8541SAndrew Gallatin 	dst->addr_low = low;
230473c7c83fSAndrew Gallatin 	wmb();
23055e7d8541SAndrew Gallatin }
23065e7d8541SAndrew Gallatin 
2307b2fc195eSAndrew Gallatin static int
23081e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2309b2fc195eSAndrew Gallatin {
2310b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2311b2fc195eSAndrew Gallatin 	struct mbuf *m;
23121e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2313b2fc195eSAndrew Gallatin 	int cnt, err;
2314b2fc195eSAndrew Gallatin 
2315b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2316b2fc195eSAndrew Gallatin 	if (m == NULL) {
2317b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2318b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2319b2fc195eSAndrew Gallatin 		goto done;
2320b2fc195eSAndrew Gallatin 	}
2321b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2322b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2323b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2324b2fc195eSAndrew Gallatin 	if (err != 0) {
2325b2fc195eSAndrew Gallatin 		m_free(m);
2326b2fc195eSAndrew Gallatin 		goto done;
2327b2fc195eSAndrew Gallatin 	}
2328b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2329b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23306d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2331b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23326d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2333b2fc195eSAndrew Gallatin 
2334b2fc195eSAndrew Gallatin done:
2335adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2336adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2337b2fc195eSAndrew Gallatin 	return err;
2338b2fc195eSAndrew Gallatin }
2339b2fc195eSAndrew Gallatin 
2340b2fc195eSAndrew Gallatin static int
23411e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2342b2fc195eSAndrew Gallatin {
2343053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2344b2fc195eSAndrew Gallatin 	struct mbuf *m;
23451e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2346053e637fSAndrew Gallatin 	int cnt, err, i;
2347b2fc195eSAndrew Gallatin 
2348a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2349a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2350a0394e33SAndrew Gallatin 	else
2351053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2352b2fc195eSAndrew Gallatin 	if (m == NULL) {
2353b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2354b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2355b2fc195eSAndrew Gallatin 		goto done;
2356b2fc195eSAndrew Gallatin 	}
23574d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2358b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2359053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2360b2fc195eSAndrew Gallatin 	if (err != 0) {
2361b2fc195eSAndrew Gallatin 		m_free(m);
2362b2fc195eSAndrew Gallatin 		goto done;
2363b2fc195eSAndrew Gallatin 	}
2364b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2365b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2366b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2367b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2368b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2369053e637fSAndrew Gallatin 
2370b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2371b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2372053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2373053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2374053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2375053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2376053e637fSAndrew Gallatin        }
2377b0f7b922SAndrew Gallatin #endif
2378b2fc195eSAndrew Gallatin 
2379b2fc195eSAndrew Gallatin done:
2380053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2381b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
23825e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
23835e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2384b2fc195eSAndrew Gallatin 		}
2385053e637fSAndrew Gallatin 		idx++;
2386053e637fSAndrew Gallatin 	}
2387b2fc195eSAndrew Gallatin 	return err;
2388b2fc195eSAndrew Gallatin }
2389b2fc195eSAndrew Gallatin 
23909b03b0f3SAndrew Gallatin /*
23919b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
23929b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
23939b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
23949b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2395053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2396053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
23979b03b0f3SAndrew Gallatin  */
23989b03b0f3SAndrew Gallatin 
2399053e637fSAndrew Gallatin static inline uint16_t
2400053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2401053e637fSAndrew Gallatin {
2402053e637fSAndrew Gallatin 	struct ether_header *eh;
2403053e637fSAndrew Gallatin 	struct ip *ip;
2404053e637fSAndrew Gallatin 	uint16_t c;
2405053e637fSAndrew Gallatin 
2406053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2407053e637fSAndrew Gallatin 
2408053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2409053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2410053e637fSAndrew Gallatin 		return 1;
2411053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2412053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2413053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2414053e637fSAndrew Gallatin 		return 1;
2415eb6219e3SAndrew Gallatin #ifdef INET
2416053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2417053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2418053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2419eb6219e3SAndrew Gallatin #else
2420eb6219e3SAndrew Gallatin 	c = 1;
2421eb6219e3SAndrew Gallatin #endif
2422053e637fSAndrew Gallatin 	c ^= 0xffff;
2423053e637fSAndrew Gallatin 	return (c);
24245e7d8541SAndrew Gallatin }
2425053e637fSAndrew Gallatin 
2426c792928fSAndrew Gallatin static void
2427c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2428c792928fSAndrew Gallatin {
2429c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2430c792928fSAndrew Gallatin 	struct ether_header *eh;
2431c792928fSAndrew Gallatin 	uint32_t partial;
2432c792928fSAndrew Gallatin 
2433c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2434c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2435c792928fSAndrew Gallatin 
2436c792928fSAndrew Gallatin 	/*
2437c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2438c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2439c792928fSAndrew Gallatin 	 * header.
2440c792928fSAndrew Gallatin 	 */
2441c792928fSAndrew Gallatin 
2442c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2443c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2444c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2445c792928fSAndrew Gallatin 	(*csum) += ~partial;
2446c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2447c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2448c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2449c792928fSAndrew Gallatin 
2450c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2451c792928fSAndrew Gallatin 	   later consumers expect this */
2452c792928fSAndrew Gallatin 	*csum = htons(*csum);
2453c792928fSAndrew Gallatin 
2454c792928fSAndrew Gallatin 	/* save the tag */
245537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2456c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
245737d89b0cSAndrew Gallatin #else
245837d89b0cSAndrew Gallatin 	{
245937d89b0cSAndrew Gallatin 		struct m_tag *mtag;
246037d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
246137d89b0cSAndrew Gallatin 				   M_NOWAIT);
246237d89b0cSAndrew Gallatin 		if (mtag == NULL)
246337d89b0cSAndrew Gallatin 			return;
246437d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
246537d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
246637d89b0cSAndrew Gallatin 	}
246737d89b0cSAndrew Gallatin 
246837d89b0cSAndrew Gallatin #endif
246937d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2470c792928fSAndrew Gallatin 
2471c792928fSAndrew Gallatin 	/*
2472c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2473c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2474c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2475c792928fSAndrew Gallatin 	 * type field is already in place.
2476c792928fSAndrew Gallatin 	 */
2477c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2478c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2479c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2480c792928fSAndrew Gallatin }
2481c792928fSAndrew Gallatin 
24825e7d8541SAndrew Gallatin 
24835e7d8541SAndrew Gallatin static inline void
24841e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2485b2fc195eSAndrew Gallatin {
24861e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2487b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2488053e637fSAndrew Gallatin 	struct mbuf *m;
2489c792928fSAndrew Gallatin 	struct ether_header *eh;
24901e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2491053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2492b2fc195eSAndrew Gallatin 	int idx;
2493053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2494b2fc195eSAndrew Gallatin 
24951e413cf9SAndrew Gallatin 	sc = ss->sc;
2496b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
24971e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2498b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2499053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2500b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2501b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2502b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25031e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2504053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2505053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2506053e637fSAndrew Gallatin 		return;
2507b2fc195eSAndrew Gallatin 	}
2508053e637fSAndrew Gallatin 
2509b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2510b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2511b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2512b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2513b2fc195eSAndrew Gallatin 
2514b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2515b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2516b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2517b2fc195eSAndrew Gallatin 
2518053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2519053e637fSAndrew Gallatin 	 * aligned */
25205e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2521b2fc195eSAndrew Gallatin 
2522053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2523053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25241e413cf9SAndrew Gallatin 	ss->ipackets++;
2525c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2526c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2527c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2528c792928fSAndrew Gallatin 	}
2529b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2530053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25311e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2532b2fc195eSAndrew Gallatin 			return;
2533053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2534053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2535053e637fSAndrew Gallatin 		   checksum is good */
2536053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2537053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2538b2fc195eSAndrew Gallatin 	}
2539c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2540c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2541c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2542c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2543c6cb3e3fSAndrew Gallatin 	}
2544053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2545053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2546b2fc195eSAndrew Gallatin }
2547b2fc195eSAndrew Gallatin 
2548b2fc195eSAndrew Gallatin static inline void
25491e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2550b2fc195eSAndrew Gallatin {
25511e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2552b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2553c792928fSAndrew Gallatin 	struct ether_header *eh;
2554b2fc195eSAndrew Gallatin 	struct mbuf *m;
25551e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2556b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2557b2fc195eSAndrew Gallatin 	int idx;
2558053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2559b2fc195eSAndrew Gallatin 
25601e413cf9SAndrew Gallatin 	sc = ss->sc;
2561b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25621e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2563b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2564b2fc195eSAndrew Gallatin 	rx->cnt++;
2565b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2566b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2567b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25681e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2569b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2570b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2571b2fc195eSAndrew Gallatin 		return;
2572b2fc195eSAndrew Gallatin 	}
2573b2fc195eSAndrew Gallatin 
2574b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2575b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2576b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2577b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2578b2fc195eSAndrew Gallatin 
2579b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2580b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2581b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2582b2fc195eSAndrew Gallatin 
2583b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2584b2fc195eSAndrew Gallatin 	 * aligned */
25855e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2586b2fc195eSAndrew Gallatin 
25879b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
25889b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25891e413cf9SAndrew Gallatin 	ss->ipackets++;
2590c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2591c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2592c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2593c792928fSAndrew Gallatin 	}
2594b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2595053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25961e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2597053e637fSAndrew Gallatin 			return;
2598053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2599053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2600053e637fSAndrew Gallatin 		   checksum is good */
2601053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2602053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2603053e637fSAndrew Gallatin 	}
2604c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2605c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2606c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2607c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2608c6cb3e3fSAndrew Gallatin 	}
2609b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2610b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2611b2fc195eSAndrew Gallatin }
2612b2fc195eSAndrew Gallatin 
2613b2fc195eSAndrew Gallatin static inline void
26141e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26155e7d8541SAndrew Gallatin {
26161e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26175e7d8541SAndrew Gallatin 	int limit = 0;
26185e7d8541SAndrew Gallatin 	uint16_t length;
26195e7d8541SAndrew Gallatin 	uint16_t checksum;
26205e7d8541SAndrew Gallatin 
26215e7d8541SAndrew Gallatin 
26225e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26235e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
26245e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2625053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2626b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
26271e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
26285e7d8541SAndrew Gallatin 		else
26291e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
26305e7d8541SAndrew Gallatin 		rx_done->cnt++;
2631adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
26325e7d8541SAndrew Gallatin 
26335e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2634f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
26355e7d8541SAndrew Gallatin 			break;
2636053e637fSAndrew Gallatin 	}
2637eb6219e3SAndrew Gallatin #ifdef INET
26381e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
2639eb6219e3SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
26401e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
26411e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
26425e7d8541SAndrew Gallatin 	}
2643eb6219e3SAndrew Gallatin #endif
26445e7d8541SAndrew Gallatin }
26455e7d8541SAndrew Gallatin 
26465e7d8541SAndrew Gallatin 
26475e7d8541SAndrew Gallatin static inline void
26481e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2649b2fc195eSAndrew Gallatin {
2650b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
26511e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2652b2fc195eSAndrew Gallatin 	struct mbuf *m;
2653b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2654f616ebc7SAndrew Gallatin 	int idx;
2655c6cb3e3fSAndrew Gallatin 	int *flags;
2656b2fc195eSAndrew Gallatin 
26571e413cf9SAndrew Gallatin 	tx = &ss->tx;
26581e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
26595e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2660b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2661b2fc195eSAndrew Gallatin 		tx->done++;
2662b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2663b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2664b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2665b2fc195eSAndrew Gallatin 		if (m != NULL) {
266671032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
266771032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
266871032832SAndrew Gallatin 				ss->omcasts++;
2669c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2670b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2671b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2672b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2673b2fc195eSAndrew Gallatin 			m_freem(m);
2674b2fc195eSAndrew Gallatin 		}
26755e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
26765e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
26775e7d8541SAndrew Gallatin 			tx->pkt_done++;
26785e7d8541SAndrew Gallatin 		}
2679b2fc195eSAndrew Gallatin 	}
2680b2fc195eSAndrew Gallatin 
2681b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2682b2fc195eSAndrew Gallatin            its OK to send packets */
2683c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2684c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2685c6cb3e3fSAndrew Gallatin #else
2686c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2687c6cb3e3fSAndrew Gallatin #endif
26881e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2689c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2690c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2691c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
26921e413cf9SAndrew Gallatin 		ss->tx.wake++;
26931e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2694b2fc195eSAndrew Gallatin 	}
2695c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2696c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2697c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2698c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2699c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2700c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2701c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2702c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2703c6cb3e3fSAndrew Gallatin 			wmb();
2704c6cb3e3fSAndrew Gallatin 		}
2705c6cb3e3fSAndrew Gallatin 	}
2706c6cb3e3fSAndrew Gallatin #endif
2707c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2708c6cb3e3fSAndrew Gallatin 
2709b2fc195eSAndrew Gallatin }
2710b2fc195eSAndrew Gallatin 
271101638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2712c587e59fSAndrew Gallatin {
2713c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2714c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2715c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2716c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
271701638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2718c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2719c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2720c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2721c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2722c587e59fSAndrew Gallatin };
272301638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
272401638550SAndrew Gallatin {
27254ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
272601638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
272701638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
272801638550SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
272901638550SAndrew Gallatin };
2730c587e59fSAndrew Gallatin 
2731c587e59fSAndrew Gallatin static void
2732c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2733c587e59fSAndrew Gallatin {
2734c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2735c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2736c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2737c587e59fSAndrew Gallatin }
2738c587e59fSAndrew Gallatin 
2739c587e59fSAndrew Gallatin 
2740c587e59fSAndrew Gallatin /*
2741c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2742c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2743c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2744c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2745c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2746c587e59fSAndrew Gallatin  * once, not each time the link is up.
2747c587e59fSAndrew Gallatin  */
2748c587e59fSAndrew Gallatin static void
2749c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2750c587e59fSAndrew Gallatin {
2751c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
275201638550SAndrew Gallatin 	char *cage_type;
2753c587e59fSAndrew Gallatin 	char *ptr;
275401638550SAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
275501638550SAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
275601638550SAndrew Gallatin 	uint32_t byte;
2757c587e59fSAndrew Gallatin 
2758c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2759c587e59fSAndrew Gallatin 
2760c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2761c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2762c587e59fSAndrew Gallatin 		return;
2763c587e59fSAndrew Gallatin 
2764c587e59fSAndrew Gallatin 	/*
2765c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2766c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2767c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2768c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2769c587e59fSAndrew Gallatin 	 */
2770c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2771c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2772c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2773c587e59fSAndrew Gallatin 	}
2774c587e59fSAndrew Gallatin 
2775c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
277637d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2777c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2778c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2779c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2780c587e59fSAndrew Gallatin 			return;
2781c587e59fSAndrew Gallatin 		}
2782c587e59fSAndrew Gallatin 	}
2783c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
278401638550SAndrew Gallatin 		/* -C is CX4 */
2785c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2786c587e59fSAndrew Gallatin 		return;
2787c587e59fSAndrew Gallatin 	}
2788c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
278901638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2790c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2791c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2792c587e59fSAndrew Gallatin 		return;
2793c587e59fSAndrew Gallatin 	}
2794c587e59fSAndrew Gallatin 
279501638550SAndrew Gallatin 	if (*ptr == 'R') {
279601638550SAndrew Gallatin 		/* -R is XFP */
279701638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
279801638550SAndrew Gallatin 		mxge_media_type_entries =
279901638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
280001638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
280101638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
280201638550SAndrew Gallatin 		cage_type = "XFP";
280301638550SAndrew Gallatin 	}
280401638550SAndrew Gallatin 
280501638550SAndrew Gallatin 	if (*ptr == 'S' || *(ptr +1) == 'S') {
280601638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
280701638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
280801638550SAndrew Gallatin 		mxge_media_type_entries =
280901638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
281001638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
281101638550SAndrew Gallatin 		cage_type = "SFP+";
281201638550SAndrew Gallatin 		byte = 3;
281301638550SAndrew Gallatin 	}
281401638550SAndrew Gallatin 
281501638550SAndrew Gallatin 	if (mxge_media_types == NULL) {
2816c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2817c587e59fSAndrew Gallatin 		return;
2818c587e59fSAndrew Gallatin 	}
2819c587e59fSAndrew Gallatin 
2820c587e59fSAndrew Gallatin 	/*
2821c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2822c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2823c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2824c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2825c587e59fSAndrew Gallatin 	 * a millisecond
2826c587e59fSAndrew Gallatin 	 */
2827c587e59fSAndrew Gallatin 
2828c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
282901638550SAndrew Gallatin 	cmd.data1 = byte;
283001638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
283101638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2832c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2833c587e59fSAndrew Gallatin 	}
283401638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
283501638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2836c587e59fSAndrew Gallatin 	}
2837c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2838c587e59fSAndrew Gallatin 		return;
2839c587e59fSAndrew Gallatin 	}
2840c587e59fSAndrew Gallatin 
2841c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
284201638550SAndrew Gallatin 	cmd.data0 = byte;
284301638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2844c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2845c587e59fSAndrew Gallatin 		DELAY(1000);
284601638550SAndrew Gallatin 		cmd.data0 = byte;
284701638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2848c587e59fSAndrew Gallatin 	}
2849c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
285001638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
285101638550SAndrew Gallatin 			      cage_type, err, ms);
2852c587e59fSAndrew Gallatin 		return;
2853c587e59fSAndrew Gallatin 	}
2854c587e59fSAndrew Gallatin 
2855c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2856c587e59fSAndrew Gallatin 		if (mxge_verbose)
285701638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2858c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2859c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2860c587e59fSAndrew Gallatin 		return;
2861c587e59fSAndrew Gallatin 	}
286201638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2863c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2864c587e59fSAndrew Gallatin 			if (mxge_verbose)
286501638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
286601638550SAndrew Gallatin 					      cage_type,
2867c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2868c587e59fSAndrew Gallatin 
2869c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2870c587e59fSAndrew Gallatin 			return;
2871c587e59fSAndrew Gallatin 		}
2872c587e59fSAndrew Gallatin 	}
287301638550SAndrew Gallatin 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
287401638550SAndrew Gallatin 		      cmd.data0);
2875c587e59fSAndrew Gallatin 
2876c587e59fSAndrew Gallatin 	return;
2877c587e59fSAndrew Gallatin }
2878c587e59fSAndrew Gallatin 
2879b2fc195eSAndrew Gallatin static void
28806d87a65dSAndrew Gallatin mxge_intr(void *arg)
2881b2fc195eSAndrew Gallatin {
28821e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
28831e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
28841e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
28851e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
28861e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
28875e7d8541SAndrew Gallatin 	uint32_t send_done_count;
28885e7d8541SAndrew Gallatin 	uint8_t valid;
2889b2fc195eSAndrew Gallatin 
2890b2fc195eSAndrew Gallatin 
2891c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
28921e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
28931e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
28941e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
28951e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
28961e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
28971e413cf9SAndrew Gallatin 		return;
28981e413cf9SAndrew Gallatin 	}
2899c6cb3e3fSAndrew Gallatin #endif
29001e413cf9SAndrew Gallatin 
29015e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29025e7d8541SAndrew Gallatin 	if (!stats->valid) {
29035e7d8541SAndrew Gallatin 		return;
2904b2fc195eSAndrew Gallatin 	}
29055e7d8541SAndrew Gallatin 	valid = stats->valid;
2906b2fc195eSAndrew Gallatin 
290791ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29085e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29095e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29105e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29115e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29125e7d8541SAndrew Gallatin 			stats->valid = 0;
2913dc8731d4SAndrew Gallatin 	} else {
2914dc8731d4SAndrew Gallatin 		stats->valid = 0;
2915dc8731d4SAndrew Gallatin 	}
2916dc8731d4SAndrew Gallatin 
2917dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29185e7d8541SAndrew Gallatin 	do {
29195e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29205e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29215e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29225e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2923c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29241e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29251e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
29265e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2927b2fc195eSAndrew Gallatin 		}
292891ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
292973c7c83fSAndrew Gallatin 			wmb();
29305e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2931b2fc195eSAndrew Gallatin 
2932c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
2933c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
29345e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
29355e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2936b2fc195eSAndrew Gallatin 			if (sc->link_state) {
29375e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
29385e7d8541SAndrew Gallatin 				if (mxge_verbose)
29395e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2940b2fc195eSAndrew Gallatin 			} else {
29415e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
29425e7d8541SAndrew Gallatin 				if (mxge_verbose)
29435e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2944b2fc195eSAndrew Gallatin 			}
2945c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2946b2fc195eSAndrew Gallatin 		}
2947b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
29481e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2949b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
29501e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
29515e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
29525e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
29535e7d8541SAndrew Gallatin 		}
2954c587e59fSAndrew Gallatin 
2955c587e59fSAndrew Gallatin 		if (stats->link_down) {
29565e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
2957c587e59fSAndrew Gallatin 			sc->link_state = 0;
2958c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2959c587e59fSAndrew Gallatin 		}
2960b2fc195eSAndrew Gallatin 	}
2961b2fc195eSAndrew Gallatin 
29625e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
29635e7d8541SAndrew Gallatin 	if (valid & 0x1)
29641e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
29651e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
2966b2fc195eSAndrew Gallatin }
2967b2fc195eSAndrew Gallatin 
2968b2fc195eSAndrew Gallatin static void
29696d87a65dSAndrew Gallatin mxge_init(void *arg)
2970b2fc195eSAndrew Gallatin {
2971b2fc195eSAndrew Gallatin }
2972b2fc195eSAndrew Gallatin 
2973b2fc195eSAndrew Gallatin 
2974b2fc195eSAndrew Gallatin 
2975b2fc195eSAndrew Gallatin static void
29761e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
29771e413cf9SAndrew Gallatin {
29781e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
29791e413cf9SAndrew Gallatin 	int i;
29801e413cf9SAndrew Gallatin 
29811e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
29821e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
29831e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
29841e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
29851e413cf9SAndrew Gallatin 	}
29861e413cf9SAndrew Gallatin 
29871e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
29881e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
29891e413cf9SAndrew Gallatin 			continue;
29901e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
29911e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
29921e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
29931e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
29941e413cf9SAndrew Gallatin 	}
29951e413cf9SAndrew Gallatin 
29961e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
29971e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
29981e413cf9SAndrew Gallatin 			continue;
29991e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30001e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30011e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30021e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30031e413cf9SAndrew Gallatin 	}
30041e413cf9SAndrew Gallatin 
30051e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30061e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30071e413cf9SAndrew Gallatin 		return;
30081e413cf9SAndrew Gallatin 
30091e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30101e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30111e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30121e413cf9SAndrew Gallatin 			continue;
30131e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30141e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30151e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30161e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30171e413cf9SAndrew Gallatin 	}
30181e413cf9SAndrew Gallatin }
30191e413cf9SAndrew Gallatin 
30201e413cf9SAndrew Gallatin static void
30216d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3022b2fc195eSAndrew Gallatin {
30231e413cf9SAndrew Gallatin 	int slice;
30241e413cf9SAndrew Gallatin 
30251e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
30261e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
30271e413cf9SAndrew Gallatin }
30281e413cf9SAndrew Gallatin 
30291e413cf9SAndrew Gallatin static void
30301e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
30311e413cf9SAndrew Gallatin {
3032b2fc195eSAndrew Gallatin 	int i;
3033b2fc195eSAndrew Gallatin 
3034b2fc195eSAndrew Gallatin 
30351e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
30361e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
30371e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3038b2fc195eSAndrew Gallatin 
30391e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
30401e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
30411e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
30421e413cf9SAndrew Gallatin 
30431e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
30441e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
30451e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
30461e413cf9SAndrew Gallatin 
30471e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
30481e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
30491e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
30501e413cf9SAndrew Gallatin 
30511e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
30521e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
30531e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
30541e413cf9SAndrew Gallatin 
30551e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
30561e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
30571e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
30581e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
30591e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3060b2fc195eSAndrew Gallatin 			}
30611e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
30621e413cf9SAndrew Gallatin 		}
30631e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
30641e413cf9SAndrew Gallatin 	}
30651e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
30661e413cf9SAndrew Gallatin 
30671e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
30681e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
30691e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
30701e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
30711e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
30721e413cf9SAndrew Gallatin 			}
30731e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
30741e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
30751e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
30761e413cf9SAndrew Gallatin 		}
30771e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
30781e413cf9SAndrew Gallatin 	}
30791e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
30801e413cf9SAndrew Gallatin 
30811e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
30821e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
30831e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
30841e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
30851e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
30861e413cf9SAndrew Gallatin 			}
30871e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
30881e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
30891e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
30901e413cf9SAndrew Gallatin 		}
30911e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
30921e413cf9SAndrew Gallatin 	}
30931e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3094b2fc195eSAndrew Gallatin }
3095b2fc195eSAndrew Gallatin 
3096b2fc195eSAndrew Gallatin static void
30976d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3098b2fc195eSAndrew Gallatin {
30991e413cf9SAndrew Gallatin 	int slice;
3100b2fc195eSAndrew Gallatin 
31011e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31021e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3103c2657176SAndrew Gallatin }
3104b2fc195eSAndrew Gallatin 
3105b2fc195eSAndrew Gallatin static int
31061e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31071e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3108b2fc195eSAndrew Gallatin {
31091e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31101e413cf9SAndrew Gallatin 	size_t bytes;
31111e413cf9SAndrew Gallatin 	int err, i;
3112b2fc195eSAndrew Gallatin 
3113b2fc195eSAndrew Gallatin 	err = ENOMEM;
3114b2fc195eSAndrew Gallatin 
31151e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3116adae7080SAndrew Gallatin 
31171e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31181e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3119aed8e389SAndrew Gallatin 
3120b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31211e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31221e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31231e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
31241e413cf9SAndrew Gallatin 		return err;;
3125b2fc195eSAndrew Gallatin 
31261e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31271e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31281e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
31291e413cf9SAndrew Gallatin 		return err;;
3130b2fc195eSAndrew Gallatin 
31311e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
31321e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31331e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31341e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
31351e413cf9SAndrew Gallatin 		return err;;
3136b2fc195eSAndrew Gallatin 
31371e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
31381e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31391e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
31401e413cf9SAndrew Gallatin 		return err;;
3141b2fc195eSAndrew Gallatin 
31421e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3143b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3144b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3145b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3146b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3147b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3148b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3149b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3150b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3151b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3152b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3153b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
31541e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3155b2fc195eSAndrew Gallatin 	if (err != 0) {
3156b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3157b2fc195eSAndrew Gallatin 			      err);
31581e413cf9SAndrew Gallatin 		return err;;
3159b2fc195eSAndrew Gallatin 	}
3160b2fc195eSAndrew Gallatin 
3161b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3162b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3163b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3164b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3165b0f7b922SAndrew Gallatin #else
3166b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3167b0f7b922SAndrew Gallatin #endif
3168b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3169b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3170b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3171053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3172b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3173053e637fSAndrew Gallatin 				 3,			/* num segs */
3174b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3175b0f7b922SAndrew Gallatin #else
3176b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3177b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3178b0f7b922SAndrew Gallatin #endif
3179b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3180b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
31811e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3182b2fc195eSAndrew Gallatin 	if (err != 0) {
3183b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3184b2fc195eSAndrew Gallatin 			      err);
31851e413cf9SAndrew Gallatin 		return err;;
3186b2fc195eSAndrew Gallatin 	}
31871e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31881e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
31891e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3190b2fc195eSAndrew Gallatin 		if (err != 0) {
3191b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3192b2fc195eSAndrew Gallatin 				      err);
31931e413cf9SAndrew Gallatin 			return err;;
3194b2fc195eSAndrew Gallatin 		}
3195b2fc195eSAndrew Gallatin 	}
31961e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
31971e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3198b2fc195eSAndrew Gallatin 	if (err != 0) {
3199b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3200b2fc195eSAndrew Gallatin 			      err);
32011e413cf9SAndrew Gallatin 		return err;;
3202b2fc195eSAndrew Gallatin 	}
3203b2fc195eSAndrew Gallatin 
32041e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32051e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32061e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3207b2fc195eSAndrew Gallatin 		if (err != 0) {
3208b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3209b2fc195eSAndrew Gallatin 				      err);
32101e413cf9SAndrew Gallatin 			return err;;
3211b2fc195eSAndrew Gallatin 		}
3212b2fc195eSAndrew Gallatin 	}
32131e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32141e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3215b2fc195eSAndrew Gallatin 	if (err != 0) {
3216b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3217b2fc195eSAndrew Gallatin 			      err);
32181e413cf9SAndrew Gallatin 		return err;;
32191e413cf9SAndrew Gallatin 	}
32201e413cf9SAndrew Gallatin 
32211e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
32221e413cf9SAndrew Gallatin 
3223c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
32241e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
32251e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
32261e413cf9SAndrew Gallatin 		return 0;
3227c6cb3e3fSAndrew Gallatin #endif
32281e413cf9SAndrew Gallatin 
32291e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32301e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32311e413cf9SAndrew Gallatin 
32321e413cf9SAndrew Gallatin 
32331e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32341e413cf9SAndrew Gallatin 	bytes = 8 +
32351e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32361e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32371e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
32381e413cf9SAndrew Gallatin 		return err;;
32391e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32401e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
32411e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
32421e413cf9SAndrew Gallatin 
32431e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32441e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32451e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32461e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
32471e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
32481e413cf9SAndrew Gallatin 		return err;;
32491e413cf9SAndrew Gallatin 
32501e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
32511e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
32521e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32531e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
32541e413cf9SAndrew Gallatin 		return err;;
32551e413cf9SAndrew Gallatin 
32561e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
32571e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
32581e413cf9SAndrew Gallatin 				 1,			/* alignment */
32591e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
32601e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
32611e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
32621e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
32631e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
32641e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
32651e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
32661e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
32671e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
32681e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
32691e413cf9SAndrew Gallatin 
32701e413cf9SAndrew Gallatin 	if (err != 0) {
32711e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
32721e413cf9SAndrew Gallatin 			      err);
32731e413cf9SAndrew Gallatin 		return err;;
32741e413cf9SAndrew Gallatin 	}
32751e413cf9SAndrew Gallatin 
32761e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
32771e413cf9SAndrew Gallatin 	   in the ring */
32781e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
32791e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
32801e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
32811e413cf9SAndrew Gallatin 		if (err != 0) {
32821e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
32831e413cf9SAndrew Gallatin 				      err);
32841e413cf9SAndrew Gallatin 			return err;;
32851e413cf9SAndrew Gallatin 		}
3286b2fc195eSAndrew Gallatin 	}
3287b2fc195eSAndrew Gallatin 	return 0;
3288b2fc195eSAndrew Gallatin 
3289b2fc195eSAndrew Gallatin }
3290b2fc195eSAndrew Gallatin 
32911e413cf9SAndrew Gallatin static int
32921e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
32931e413cf9SAndrew Gallatin {
32941e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
32951e413cf9SAndrew Gallatin 	int tx_ring_size;
32961e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
32971e413cf9SAndrew Gallatin 	int err, slice;
32981e413cf9SAndrew Gallatin 
32991e413cf9SAndrew Gallatin 	/* get ring sizes */
33001e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33011e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33021e413cf9SAndrew Gallatin 	if (err != 0) {
33031e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33041e413cf9SAndrew Gallatin 		goto abort;
33051e413cf9SAndrew Gallatin 	}
33061e413cf9SAndrew Gallatin 
33071e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33081e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
33091e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
33101e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
33111e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
33121e413cf9SAndrew Gallatin 
33131e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33141e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33151e413cf9SAndrew Gallatin 					     rx_ring_entries,
33161e413cf9SAndrew Gallatin 					     tx_ring_entries);
33171e413cf9SAndrew Gallatin 		if (err != 0)
33181e413cf9SAndrew Gallatin 			goto abort;
33191e413cf9SAndrew Gallatin 	}
33201e413cf9SAndrew Gallatin 	return 0;
33211e413cf9SAndrew Gallatin 
33221e413cf9SAndrew Gallatin abort:
33231e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33241e413cf9SAndrew Gallatin 	return err;
33251e413cf9SAndrew Gallatin 
33261e413cf9SAndrew Gallatin }
33271e413cf9SAndrew Gallatin 
33281e413cf9SAndrew Gallatin 
3329053e637fSAndrew Gallatin static void
3330053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3331053e637fSAndrew Gallatin {
3332c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3333053e637fSAndrew Gallatin 
3334053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3335053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3336053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3337053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3338053e637fSAndrew Gallatin 		*nbufs = 1;
3339053e637fSAndrew Gallatin 		return;
3340053e637fSAndrew Gallatin 	}
3341053e637fSAndrew Gallatin 
3342053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3343053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3344053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3345053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3346053e637fSAndrew Gallatin 		*nbufs = 1;
3347053e637fSAndrew Gallatin 		return;
3348053e637fSAndrew Gallatin 	}
3349b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3350053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3351053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3352053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3353053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3354053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3355053e637fSAndrew Gallatin 	if (*nbufs == 3)
3356053e637fSAndrew Gallatin 		*nbufs = 4;
3357b0f7b922SAndrew Gallatin #else
3358b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3359b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3360b0f7b922SAndrew Gallatin 	*nbufs = 1;
3361b0f7b922SAndrew Gallatin #endif
3362053e637fSAndrew Gallatin }
3363053e637fSAndrew Gallatin 
3364b2fc195eSAndrew Gallatin static int
33651e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3366b2fc195eSAndrew Gallatin {
33671e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
33686d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3369b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3370053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
33711e413cf9SAndrew Gallatin 	int err, i, slice;
3372b2fc195eSAndrew Gallatin 
33731e413cf9SAndrew Gallatin 
33741e413cf9SAndrew Gallatin 	sc = ss->sc;
33751e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
33761e413cf9SAndrew Gallatin 
33771e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
33781e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3379053e637fSAndrew Gallatin 
3380053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3381053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
33821e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
33831e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3384053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3385053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3386053e637fSAndrew Gallatin 			break;
3387053e637fSAndrew Gallatin 		}
33881e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3389053e637fSAndrew Gallatin 	}
33901e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
33911e413cf9SAndrew Gallatin 
33921e413cf9SAndrew Gallatin 	err = 0;
3393c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33941e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
33951e413cf9SAndrew Gallatin 	if (slice == 0) {
3396c6cb3e3fSAndrew Gallatin #endif
33971e413cf9SAndrew Gallatin 		cmd.data0 = slice;
33981e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
33991e413cf9SAndrew Gallatin 		ss->tx.lanai =
34001e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3401c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3402c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3403c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3404c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3405c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34061e413cf9SAndrew Gallatin 	}
3407c6cb3e3fSAndrew Gallatin #endif
34081e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34091e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34101e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34111e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34121e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34131e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34141e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34151e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34161e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34171e413cf9SAndrew Gallatin 
34181e413cf9SAndrew Gallatin 	if (err != 0) {
34191e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34201e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34211e413cf9SAndrew Gallatin 		return EIO;
34221e413cf9SAndrew Gallatin 	}
34231e413cf9SAndrew Gallatin 
34241e413cf9SAndrew Gallatin 	/* stock receive rings */
34251e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34261e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34271e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34281e413cf9SAndrew Gallatin 		if (err) {
34291e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34301e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34311e413cf9SAndrew Gallatin 			return ENOMEM;
34321e413cf9SAndrew Gallatin 		}
34331e413cf9SAndrew Gallatin 	}
34341e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34351e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34361e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34371e413cf9SAndrew Gallatin 	}
34381e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34391e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
34404d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
34414d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34421e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34431e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34441e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34451e413cf9SAndrew Gallatin 		if (err) {
34461e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34471e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
34481e413cf9SAndrew Gallatin 			return ENOMEM;
34491e413cf9SAndrew Gallatin 		}
34501e413cf9SAndrew Gallatin 	}
34511e413cf9SAndrew Gallatin 	return 0;
34521e413cf9SAndrew Gallatin }
34531e413cf9SAndrew Gallatin 
34541e413cf9SAndrew Gallatin static int
34551e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
34561e413cf9SAndrew Gallatin {
34571e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34581e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
34591e413cf9SAndrew Gallatin 	bus_addr_t bus;
34601e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3461c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3462b2fc195eSAndrew Gallatin 
34637d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
34647d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34657d542e2dSAndrew Gallatin 
3466adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3467b2fc195eSAndrew Gallatin 	if (err != 0) {
3468b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3469b2fc195eSAndrew Gallatin 		return EIO;
3470b2fc195eSAndrew Gallatin 	}
3471b2fc195eSAndrew Gallatin 
34721e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
34731e413cf9SAndrew Gallatin 		/* setup the indirection table */
34741e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
34751e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34761e413cf9SAndrew Gallatin 				    &cmd);
3477b2fc195eSAndrew Gallatin 
34781e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34791e413cf9SAndrew Gallatin 				     &cmd);
34801e413cf9SAndrew Gallatin 		if (err != 0) {
34811e413cf9SAndrew Gallatin 			device_printf(sc->dev,
34821e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
34831e413cf9SAndrew Gallatin 			return err;
34841e413cf9SAndrew Gallatin 		}
34851e413cf9SAndrew Gallatin 
34861e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
34871e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
34881e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
34891e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
34901e413cf9SAndrew Gallatin 
34911e413cf9SAndrew Gallatin 		cmd.data0 = 1;
34921e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
34931e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
34941e413cf9SAndrew Gallatin 		if (err != 0) {
34951e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
34961e413cf9SAndrew Gallatin 			return err;
34971e413cf9SAndrew Gallatin 		}
34981e413cf9SAndrew Gallatin 	}
34991e413cf9SAndrew Gallatin 
35001e413cf9SAndrew Gallatin 
35011e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35021e413cf9SAndrew Gallatin 
35031e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3504053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3505053e637fSAndrew Gallatin 			    &cmd);
3506053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3507053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35081e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3509053e637fSAndrew Gallatin 		device_printf(sc->dev,
3510053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35111e413cf9SAndrew Gallatin 			      nbufs);
3512053e637fSAndrew Gallatin 		return EIO;
3513053e637fSAndrew Gallatin 	}
3514b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3515b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3516b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3517c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35185e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3519b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35205e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3521b2fc195eSAndrew Gallatin 			     &cmd);
3522053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35235e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35240fa7f681SAndrew Gallatin 
35250fa7f681SAndrew Gallatin 	if (err != 0) {
35260fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35270fa7f681SAndrew Gallatin 		goto abort;
35280fa7f681SAndrew Gallatin 	}
35290fa7f681SAndrew Gallatin 
3530b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3531c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3532c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3533c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3534c6cb3e3fSAndrew Gallatin #else
3535c6cb3e3fSAndrew Gallatin 	     slice < 1;
3536c6cb3e3fSAndrew Gallatin #endif
3537c6cb3e3fSAndrew Gallatin 	     slice++) {
3538c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3539c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3540c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3541c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3542c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35430fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3544c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3545c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3546c6cb3e3fSAndrew Gallatin 	}
35470fa7f681SAndrew Gallatin 
35480fa7f681SAndrew Gallatin 	if (err != 0) {
35491e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
35500fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
35510fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35520fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35530fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
35540fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35550fa7f681SAndrew Gallatin 				    &cmd);
35560fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
35570fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
35580fa7f681SAndrew Gallatin 	} else {
35590fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
35600fa7f681SAndrew Gallatin 	}
3561b2fc195eSAndrew Gallatin 
3562b2fc195eSAndrew Gallatin 	if (err != 0) {
3563b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3564b2fc195eSAndrew Gallatin 		goto abort;
3565b2fc195eSAndrew Gallatin 	}
3566b2fc195eSAndrew Gallatin 
35671e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
35681e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35691e413cf9SAndrew Gallatin 		if (err != 0) {
35701e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
35711e413cf9SAndrew Gallatin 				      slice);
35721e413cf9SAndrew Gallatin 			goto abort;
35731e413cf9SAndrew Gallatin 		}
35741e413cf9SAndrew Gallatin 	}
35751e413cf9SAndrew Gallatin 
3576b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
35775e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3578b2fc195eSAndrew Gallatin 	if (err) {
3579b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3580b2fc195eSAndrew Gallatin 		goto abort;
3581b2fc195eSAndrew Gallatin 	}
3582c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3583c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3584c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3585c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3586c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3587c6cb3e3fSAndrew Gallatin 	}
3588c6cb3e3fSAndrew Gallatin #endif
3589b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3590b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3591e749ef6bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3592b2fc195eSAndrew Gallatin 
3593b2fc195eSAndrew Gallatin 	return 0;
3594b2fc195eSAndrew Gallatin 
3595b2fc195eSAndrew Gallatin 
3596b2fc195eSAndrew Gallatin abort:
35976d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3598a98d6cd7SAndrew Gallatin 
3599b2fc195eSAndrew Gallatin 	return err;
3600b2fc195eSAndrew Gallatin }
3601b2fc195eSAndrew Gallatin 
3602b2fc195eSAndrew Gallatin static int
36036d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3604b2fc195eSAndrew Gallatin {
36056d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3606b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3607c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3608c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3609c6cb3e3fSAndrew Gallatin 	int slice;
3610c6cb3e3fSAndrew Gallatin #endif
3611b2fc195eSAndrew Gallatin 
3612e749ef6bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3613c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3614c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3615c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3616c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3617c6cb3e3fSAndrew Gallatin 	}
3618c6cb3e3fSAndrew Gallatin #endif
3619b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3620b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
362173c7c83fSAndrew Gallatin 	wmb();
36225e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3623b2fc195eSAndrew Gallatin 	if (err) {
3624b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3625b2fc195eSAndrew Gallatin 	}
3626b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3627b2fc195eSAndrew Gallatin 		/* wait for down irq */
3628dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3629b2fc195eSAndrew Gallatin 	}
363073c7c83fSAndrew Gallatin 	wmb();
3631b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3632b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3633b2fc195eSAndrew Gallatin 	}
3634a98d6cd7SAndrew Gallatin 
36356d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3636a98d6cd7SAndrew Gallatin 
3637b2fc195eSAndrew Gallatin 	return 0;
3638b2fc195eSAndrew Gallatin }
3639b2fc195eSAndrew Gallatin 
3640dce01b9bSAndrew Gallatin static void
3641dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3642dce01b9bSAndrew Gallatin {
3643dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3644dce01b9bSAndrew Gallatin 	int reg;
3645dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3646dce01b9bSAndrew Gallatin 
3647dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3648dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3649dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3650dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3651dce01b9bSAndrew Gallatin 
3652dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3653dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3654dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3655dce01b9bSAndrew Gallatin 	}
3656dce01b9bSAndrew Gallatin 
3657dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3658dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3659dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3660dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3661dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3662dce01b9bSAndrew Gallatin }
3663dce01b9bSAndrew Gallatin 
3664dce01b9bSAndrew Gallatin static uint32_t
3665dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3666dce01b9bSAndrew Gallatin {
3667dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3668dce01b9bSAndrew Gallatin 	uint32_t vs;
3669dce01b9bSAndrew Gallatin 
3670dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3671dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3672dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3673dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3674dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3675dce01b9bSAndrew Gallatin 	}
3676dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3677dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3678dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3679dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3680dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3681dce01b9bSAndrew Gallatin }
3682dce01b9bSAndrew Gallatin 
3683e749ef6bSAndrew Gallatin static int
3684c6cb3e3fSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc, int slice)
3685dce01b9bSAndrew Gallatin {
3686e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3687c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
3688dce01b9bSAndrew Gallatin 	int err;
3689dce01b9bSAndrew Gallatin 	uint32_t reboot;
3690dce01b9bSAndrew Gallatin 	uint16_t cmd;
3691dce01b9bSAndrew Gallatin 
3692dce01b9bSAndrew Gallatin 	err = ENXIO;
3693dce01b9bSAndrew Gallatin 
3694dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3695dce01b9bSAndrew Gallatin 
3696dce01b9bSAndrew Gallatin 	/*
3697dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3698dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3699dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3700dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3701dce01b9bSAndrew Gallatin 	 * again
3702dce01b9bSAndrew Gallatin 	 */
3703dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3704dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3705dce01b9bSAndrew Gallatin 		/*
3706dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3707dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3708dce01b9bSAndrew Gallatin 		 * back, then give up
3709dce01b9bSAndrew Gallatin 		 */
3710dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3711dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3712dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3713dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3714e749ef6bSAndrew Gallatin 			return (err);
3715dce01b9bSAndrew Gallatin 		}
3716dce01b9bSAndrew Gallatin 	}
3717dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3718dce01b9bSAndrew Gallatin 		/* print the reboot status */
3719dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3720dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3721dce01b9bSAndrew Gallatin 			      reboot);
3722dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3723e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3724e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3725dce01b9bSAndrew Gallatin 
3726dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3727dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
372810882804SAndrew Gallatin 
372910882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
373010882804SAndrew Gallatin 			mxge_close(sc);
373110882804SAndrew Gallatin 			err = mxge_open(sc);
373210882804SAndrew Gallatin 		}
3733dce01b9bSAndrew Gallatin 	} else {
3734c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
3735c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3736c6cb3e3fSAndrew Gallatin 			      "NIC did not reboot, slice %d ring state:\n",
3737c6cb3e3fSAndrew Gallatin 			      slice);
3738c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3739c6cb3e3fSAndrew Gallatin 			      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3740c6cb3e3fSAndrew Gallatin 			      tx->req, tx->done, tx->queue_active);
3741c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3742c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3743dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3744c6cb3e3fSAndrew Gallatin 			      tx->pkt_done,
37451e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
374610882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3747dce01b9bSAndrew Gallatin 	}
3748e749ef6bSAndrew Gallatin 	return (err);
3749dce01b9bSAndrew Gallatin }
3750dce01b9bSAndrew Gallatin 
3751e749ef6bSAndrew Gallatin static int
3752dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3753dce01b9bSAndrew Gallatin {
3754c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
37551e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3756c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3757dce01b9bSAndrew Gallatin 
3758dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3759dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3760c6cb3e3fSAndrew Gallatin 	for (i = 0;
3761c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3762c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3763c6cb3e3fSAndrew Gallatin #else
3764c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3765c6cb3e3fSAndrew Gallatin #endif
3766c6cb3e3fSAndrew Gallatin 	     i++) {
3767c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3768dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3769dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3770c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3771c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
3772c587e59fSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause)
3773c6cb3e3fSAndrew Gallatin 				err = mxge_watchdog_reset(sc, i);
3774c587e59fSAndrew Gallatin 			else
3775c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3776c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3777c587e59fSAndrew Gallatin 		}
3778dce01b9bSAndrew Gallatin 
3779dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3780dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3781c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3782c6cb3e3fSAndrew Gallatin 	}
3783c587e59fSAndrew Gallatin 
3784c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3785c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3786e749ef6bSAndrew Gallatin 	return (err);
3787dce01b9bSAndrew Gallatin }
3788dce01b9bSAndrew Gallatin 
3789dce01b9bSAndrew Gallatin static void
37901e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
37911e413cf9SAndrew Gallatin {
37921e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37931e413cf9SAndrew Gallatin 	u_long ipackets = 0;
3794c6cb3e3fSAndrew Gallatin 	u_long opackets = 0;
379571032832SAndrew Gallatin #ifdef IFNET_BUF_RING
379671032832SAndrew Gallatin 	u_long obytes = 0;
379771032832SAndrew Gallatin 	u_long omcasts = 0;
379871032832SAndrew Gallatin 	u_long odrops = 0;
379971032832SAndrew Gallatin #endif
3800c6cb3e3fSAndrew Gallatin 	u_long oerrors = 0;
38011e413cf9SAndrew Gallatin 	int slice;
38021e413cf9SAndrew Gallatin 
38031e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
38041e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
38051e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
3806c6cb3e3fSAndrew Gallatin 		opackets += ss->opackets;
380771032832SAndrew Gallatin #ifdef IFNET_BUF_RING
380871032832SAndrew Gallatin 		obytes += ss->obytes;
380971032832SAndrew Gallatin 		omcasts += ss->omcasts;
381071032832SAndrew Gallatin 		odrops += ss->tx.br->br_drops;
381171032832SAndrew Gallatin #endif
3812c6cb3e3fSAndrew Gallatin 		oerrors += ss->oerrors;
38131e413cf9SAndrew Gallatin 	}
38141e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
3815c6cb3e3fSAndrew Gallatin 	sc->ifp->if_opackets = opackets;
381671032832SAndrew Gallatin #ifdef IFNET_BUF_RING
381771032832SAndrew Gallatin 	sc->ifp->if_obytes = obytes;
381871032832SAndrew Gallatin 	sc->ifp->if_omcasts = omcasts;
381971032832SAndrew Gallatin 	sc->ifp->if_snd.ifq_drops = odrops;
382071032832SAndrew Gallatin #endif
3821c6cb3e3fSAndrew Gallatin 	sc->ifp->if_oerrors = oerrors;
38221e413cf9SAndrew Gallatin }
3823c6cb3e3fSAndrew Gallatin 
38241e413cf9SAndrew Gallatin static void
3825dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3826dce01b9bSAndrew Gallatin {
3827dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3828e749ef6bSAndrew Gallatin 	int err = 0;
3829dce01b9bSAndrew Gallatin 
38301e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
38311e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
38321e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3833e749ef6bSAndrew Gallatin 		err = mxge_watchdog(sc);
38341e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
38351e413cf9SAndrew Gallatin 	}
38361e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3837e749ef6bSAndrew Gallatin 	if (err == 0)
3838e749ef6bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3839e749ef6bSAndrew Gallatin 
3840dce01b9bSAndrew Gallatin }
3841b2fc195eSAndrew Gallatin 
3842b2fc195eSAndrew Gallatin static int
38436d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3844b2fc195eSAndrew Gallatin {
3845b2fc195eSAndrew Gallatin 	return EINVAL;
3846b2fc195eSAndrew Gallatin }
3847b2fc195eSAndrew Gallatin 
3848b2fc195eSAndrew Gallatin static int
38496d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3850b2fc195eSAndrew Gallatin {
3851b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3852b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3853b2fc195eSAndrew Gallatin 	int err = 0;
3854b2fc195eSAndrew Gallatin 
3855b2fc195eSAndrew Gallatin 
3856c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3857053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3858b2fc195eSAndrew Gallatin 		return EINVAL;
3859a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3860b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3861b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3862b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
38636d87a65dSAndrew Gallatin 		mxge_close(sc);
38646d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3865b2fc195eSAndrew Gallatin 		if (err != 0) {
3866b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
38676d87a65dSAndrew Gallatin 			mxge_close(sc);
38686d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3869b2fc195eSAndrew Gallatin 		}
3870b2fc195eSAndrew Gallatin 	}
3871a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3872b2fc195eSAndrew Gallatin 	return err;
3873b2fc195eSAndrew Gallatin }
3874b2fc195eSAndrew Gallatin 
3875b2fc195eSAndrew Gallatin static void
38766d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3877b2fc195eSAndrew Gallatin {
38786d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3879b2fc195eSAndrew Gallatin 
3880b2fc195eSAndrew Gallatin 
3881b2fc195eSAndrew Gallatin 	if (sc == NULL)
3882b2fc195eSAndrew Gallatin 		return;
3883b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3884c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3885b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3886c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3887b2fc195eSAndrew Gallatin }
3888b2fc195eSAndrew Gallatin 
3889b2fc195eSAndrew Gallatin static int
38906d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3891b2fc195eSAndrew Gallatin {
38926d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3893b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3894b2fc195eSAndrew Gallatin 	int err, mask;
3895b2fc195eSAndrew Gallatin 
3896b2fc195eSAndrew Gallatin 	err = 0;
3897b2fc195eSAndrew Gallatin 	switch (command) {
3898b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3899b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3900b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3901b2fc195eSAndrew Gallatin 		break;
3902b2fc195eSAndrew Gallatin 
3903b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
39046d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3905b2fc195eSAndrew Gallatin 		break;
3906b2fc195eSAndrew Gallatin 
3907b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3908a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
39098c5d766cSAndrew Gallatin 		if (sc->dying) {
39108c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
39118c5d766cSAndrew Gallatin 			return EINVAL;
39128c5d766cSAndrew Gallatin 		}
3913b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3914dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
39156d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3916dce01b9bSAndrew Gallatin 			} else {
39170fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
39180fa7f681SAndrew Gallatin 				   flag chages */
39190fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
39200fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
39210fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
39220fa7f681SAndrew Gallatin 			}
3923b2fc195eSAndrew Gallatin 		} else {
3924dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
39251e413cf9SAndrew Gallatin 				mxge_close(sc);
3926dce01b9bSAndrew Gallatin 			}
3927b2fc195eSAndrew Gallatin 		}
3928a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3929b2fc195eSAndrew Gallatin 		break;
3930b2fc195eSAndrew Gallatin 
3931b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3932b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3933a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
39340fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3935a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3936b2fc195eSAndrew Gallatin 		break;
3937b2fc195eSAndrew Gallatin 
3938b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3939a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3940b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3941b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3942b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3943aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3944aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3945aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3946b2fc195eSAndrew Gallatin 			} else {
3947b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3948b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3949b2fc195eSAndrew Gallatin 			}
3950b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3951b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3952b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
39535e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3954b2fc195eSAndrew Gallatin 			} else {
3955b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
39565e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3957b2fc195eSAndrew Gallatin 			}
3958b2fc195eSAndrew Gallatin 		}
3959aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3960aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3961aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3962aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3963aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3964aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3965aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3966aed8e389SAndrew Gallatin 			} else {
3967aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3968aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3969aed8e389SAndrew Gallatin 				err = EINVAL;
3970aed8e389SAndrew Gallatin 			}
3971aed8e389SAndrew Gallatin 		}
3972f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3973f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3974f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3975f04b33f8SAndrew Gallatin 			else
3976f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3977f04b33f8SAndrew Gallatin 		}
3978c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3979c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3980a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3981c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3982c792928fSAndrew Gallatin 
3983b2fc195eSAndrew Gallatin 		break;
3984b2fc195eSAndrew Gallatin 
3985b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3986b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3987b2fc195eSAndrew Gallatin 				    &sc->media, command);
3988b2fc195eSAndrew Gallatin                 break;
3989b2fc195eSAndrew Gallatin 
3990b2fc195eSAndrew Gallatin 	default:
3991b2fc195eSAndrew Gallatin 		err = ENOTTY;
3992b2fc195eSAndrew Gallatin         }
3993b2fc195eSAndrew Gallatin 	return err;
3994b2fc195eSAndrew Gallatin }
3995b2fc195eSAndrew Gallatin 
3996b2fc195eSAndrew Gallatin static void
39976d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3998b2fc195eSAndrew Gallatin {
3999b2fc195eSAndrew Gallatin 
40001e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
40016d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
40026d87a65dSAndrew Gallatin 			  &mxge_flow_control);
40036d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
40046d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
40056d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
40066d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4007d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4008d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
40095e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
40105e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
40115e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
40125e7d8541SAndrew Gallatin 			  &mxge_verbose);
4013dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
4014053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
40151e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
40161e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
401794c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4018f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
4019f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
4020f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
4021b2fc195eSAndrew Gallatin 
40225e7d8541SAndrew Gallatin 	if (bootverbose)
40235e7d8541SAndrew Gallatin 		mxge_verbose = 1;
40246d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
40256d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4026dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
40271e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
40286d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
40291e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4030bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
40311e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
4032b2fc195eSAndrew Gallatin 	}
4033f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4034f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4035f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
40361e413cf9SAndrew Gallatin }
40371e413cf9SAndrew Gallatin 
40381e413cf9SAndrew Gallatin 
40391e413cf9SAndrew Gallatin static void
40401e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
40411e413cf9SAndrew Gallatin {
40421e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
40431e413cf9SAndrew Gallatin 	int i;
40441e413cf9SAndrew Gallatin 
40451e413cf9SAndrew Gallatin 
40461e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
40471e413cf9SAndrew Gallatin 		return;
40481e413cf9SAndrew Gallatin 
40491e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40501e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
40511e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
40521e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
40531e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4054c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4055c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4056c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4057c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4058c6cb3e3fSAndrew Gallatin 			}
4059c6cb3e3fSAndrew Gallatin #endif
40601e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
40611e413cf9SAndrew Gallatin 		}
40621e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
40631e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
40641e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
40651e413cf9SAndrew Gallatin 		}
40661e413cf9SAndrew Gallatin 	}
40671e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
40681e413cf9SAndrew Gallatin 	sc->ss = NULL;
40691e413cf9SAndrew Gallatin }
40701e413cf9SAndrew Gallatin 
40711e413cf9SAndrew Gallatin static int
40721e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
40731e413cf9SAndrew Gallatin {
40741e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
40751e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
40761e413cf9SAndrew Gallatin 	size_t bytes;
40771e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
40781e413cf9SAndrew Gallatin 
40791e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40801e413cf9SAndrew Gallatin 	if (err != 0) {
40811e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
40821e413cf9SAndrew Gallatin 		return err;
40831e413cf9SAndrew Gallatin 	}
40841e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
40851e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
40861e413cf9SAndrew Gallatin 
40871e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
40881e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
40891e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
40901e413cf9SAndrew Gallatin 		return (ENOMEM);
40911e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40921e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
40931e413cf9SAndrew Gallatin 
40941e413cf9SAndrew Gallatin 		ss->sc = sc;
40951e413cf9SAndrew Gallatin 
40961e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
40971e413cf9SAndrew Gallatin 
40981e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
40991e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
41001e413cf9SAndrew Gallatin 		if (err != 0)
41011e413cf9SAndrew Gallatin 			goto abort;
41021e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
41031e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
41041e413cf9SAndrew Gallatin 
41051e413cf9SAndrew Gallatin 		/*
41061e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
41071e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
41081e413cf9SAndrew Gallatin 		 * slice for now
41091e413cf9SAndrew Gallatin 		 */
4110c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
41111e413cf9SAndrew Gallatin 		if (i > 0)
41121e413cf9SAndrew Gallatin 			continue;
4113c6cb3e3fSAndrew Gallatin #endif
41141e413cf9SAndrew Gallatin 
41151e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
41161e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
41171e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
41181e413cf9SAndrew Gallatin 		if (err != 0)
41191e413cf9SAndrew Gallatin 			goto abort;
41201e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
41211e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
41221e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
41231e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4124c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4125c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4126c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4127c6cb3e3fSAndrew Gallatin #endif
41281e413cf9SAndrew Gallatin 	}
41291e413cf9SAndrew Gallatin 
41301e413cf9SAndrew Gallatin 	return (0);
41311e413cf9SAndrew Gallatin 
41321e413cf9SAndrew Gallatin abort:
41331e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
41341e413cf9SAndrew Gallatin 	return (ENOMEM);
41351e413cf9SAndrew Gallatin }
41361e413cf9SAndrew Gallatin 
41371e413cf9SAndrew Gallatin static void
41381e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
41391e413cf9SAndrew Gallatin {
41401e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
41411e413cf9SAndrew Gallatin 	char *old_fw;
41421e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
41431e413cf9SAndrew Gallatin 
41441e413cf9SAndrew Gallatin 	sc->num_slices = 1;
41451e413cf9SAndrew Gallatin 	/*
41461e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
41471e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
41481e413cf9SAndrew Gallatin 	 */
41491e413cf9SAndrew Gallatin 
41501e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
41511e413cf9SAndrew Gallatin 		return;
41521e413cf9SAndrew Gallatin 
41531e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
41541e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
41551e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
41561e413cf9SAndrew Gallatin 		return;
41571e413cf9SAndrew Gallatin 
41581e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
41591e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
41601e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
41611e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
41621e413cf9SAndrew Gallatin 	else
41631e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
41641e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
41651e413cf9SAndrew Gallatin 	if (status != 0) {
41661e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
41671e413cf9SAndrew Gallatin 		return;
41681e413cf9SAndrew Gallatin 	}
41691e413cf9SAndrew Gallatin 
41701e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
41711e413cf9SAndrew Gallatin 	   is alive */
41721e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
41731e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
41741e413cf9SAndrew Gallatin 	if (status != 0) {
41751e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
41761e413cf9SAndrew Gallatin 		goto abort_with_fw;
41771e413cf9SAndrew Gallatin 	}
41781e413cf9SAndrew Gallatin 
41791e413cf9SAndrew Gallatin 	/* get rx ring size */
41801e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
41811e413cf9SAndrew Gallatin 	if (status != 0) {
41821e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
41831e413cf9SAndrew Gallatin 		goto abort_with_fw;
41841e413cf9SAndrew Gallatin 	}
41851e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
41861e413cf9SAndrew Gallatin 
41871e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
41881e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
41891e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
41901e413cf9SAndrew Gallatin 	if (status != 0) {
41911e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
41921e413cf9SAndrew Gallatin 		goto abort_with_fw;
41931e413cf9SAndrew Gallatin 	}
41941e413cf9SAndrew Gallatin 
41951e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
41961e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
41971e413cf9SAndrew Gallatin 	if (status != 0) {
41981e413cf9SAndrew Gallatin 		device_printf(sc->dev,
41991e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
42001e413cf9SAndrew Gallatin 		goto abort_with_fw;
42011e413cf9SAndrew Gallatin 	}
42021e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
42031e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
42041e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
42051e413cf9SAndrew Gallatin 
42061e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
42071e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
42081e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
42091e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
42101e413cf9SAndrew Gallatin 	} else {
42111e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
42121e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
42131e413cf9SAndrew Gallatin 	}
42141e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
42151e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
42161e413cf9SAndrew Gallatin 		sc->num_slices--;
42171e413cf9SAndrew Gallatin 
42181e413cf9SAndrew Gallatin 	if (mxge_verbose)
42191e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
42201e413cf9SAndrew Gallatin 			      sc->num_slices);
42211e413cf9SAndrew Gallatin 
42221e413cf9SAndrew Gallatin 	return;
42231e413cf9SAndrew Gallatin 
42241e413cf9SAndrew Gallatin abort_with_fw:
42251e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
42261e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
42271e413cf9SAndrew Gallatin }
42281e413cf9SAndrew Gallatin 
42291e413cf9SAndrew Gallatin static int
42301e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
42311e413cf9SAndrew Gallatin {
42321e413cf9SAndrew Gallatin 	size_t bytes;
42331e413cf9SAndrew Gallatin 	int count, err, i, rid;
42341e413cf9SAndrew Gallatin 
42351e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
42361e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
42371e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
42381e413cf9SAndrew Gallatin 
42391e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
42401e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
42411e413cf9SAndrew Gallatin 		return ENXIO;
42421e413cf9SAndrew Gallatin 	}
42431e413cf9SAndrew Gallatin 
42441e413cf9SAndrew Gallatin 	count = sc->num_slices;
42451e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
42461e413cf9SAndrew Gallatin 	if (err != 0) {
42471e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
42481e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
42491e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
42501e413cf9SAndrew Gallatin 	}
42511e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
42521e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
42531e413cf9SAndrew Gallatin 			      count, sc->num_slices);
42541e413cf9SAndrew Gallatin 		device_printf(sc->dev,
42551e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
42561e413cf9SAndrew Gallatin 			      count);
42571e413cf9SAndrew Gallatin 		err = ENOSPC;
42581e413cf9SAndrew Gallatin 		goto abort_with_msix;
42591e413cf9SAndrew Gallatin 	}
42601e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
42611e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42621e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
42631e413cf9SAndrew Gallatin 		err = ENOMEM;
42641e413cf9SAndrew Gallatin 		goto abort_with_msix;
42651e413cf9SAndrew Gallatin 	}
42661e413cf9SAndrew Gallatin 
42671e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42681e413cf9SAndrew Gallatin 		rid = i + 1;
42691e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
42701e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
42711e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
42721e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
42731e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
42741e413cf9SAndrew Gallatin 				      " for message %d\n", i);
42751e413cf9SAndrew Gallatin 			err = ENXIO;
42761e413cf9SAndrew Gallatin 			goto abort_with_res;
42771e413cf9SAndrew Gallatin 		}
42781e413cf9SAndrew Gallatin 	}
42791e413cf9SAndrew Gallatin 
42801e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
42811e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42821e413cf9SAndrew Gallatin 
42831e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42841e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
42851e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
428637d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
428737d89b0cSAndrew Gallatin 				     NULL,
428837d89b0cSAndrew Gallatin #endif
428937d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
42901e413cf9SAndrew Gallatin 		if (err != 0) {
42911e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
42921e413cf9SAndrew Gallatin 				      "message %d\n", i);
42931e413cf9SAndrew Gallatin 			goto abort_with_intr;
42941e413cf9SAndrew Gallatin 		}
42951e413cf9SAndrew Gallatin 	}
42961e413cf9SAndrew Gallatin 
42971e413cf9SAndrew Gallatin 	if (mxge_verbose) {
42981e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
42991e413cf9SAndrew Gallatin 			      sc->num_slices);
43001e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
43011e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
43021e413cf9SAndrew Gallatin 		printf("\n");
43031e413cf9SAndrew Gallatin 	}
43041e413cf9SAndrew Gallatin 	return (0);
43051e413cf9SAndrew Gallatin 
43061e413cf9SAndrew Gallatin abort_with_intr:
43071e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43081e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
43091e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43101e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
43111e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
43121e413cf9SAndrew Gallatin 		}
43131e413cf9SAndrew Gallatin 	}
43141e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
43151e413cf9SAndrew Gallatin 
43161e413cf9SAndrew Gallatin 
43171e413cf9SAndrew Gallatin abort_with_res:
43181e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43191e413cf9SAndrew Gallatin 		rid = i + 1;
43201e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
43211e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43221e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
43231e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
43241e413cf9SAndrew Gallatin 	}
43251e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
43261e413cf9SAndrew Gallatin 
43271e413cf9SAndrew Gallatin 
43281e413cf9SAndrew Gallatin abort_with_msix:
43291e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
43301e413cf9SAndrew Gallatin 
43311e413cf9SAndrew Gallatin abort_with_msix_table:
43321e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43331e413cf9SAndrew Gallatin 			     sc->msix_table_res);
43341e413cf9SAndrew Gallatin 
43351e413cf9SAndrew Gallatin 	return err;
43361e413cf9SAndrew Gallatin }
43371e413cf9SAndrew Gallatin 
43381e413cf9SAndrew Gallatin static int
43391e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
43401e413cf9SAndrew Gallatin {
43411e413cf9SAndrew Gallatin 	int count, err, rid;
43421e413cf9SAndrew Gallatin 
43431e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
43441e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
43451e413cf9SAndrew Gallatin 		rid = 1;
43461e413cf9SAndrew Gallatin 	} else {
43471e413cf9SAndrew Gallatin 		rid = 0;
434891ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
43491e413cf9SAndrew Gallatin 	}
43501e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
43511e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
43521e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
43531e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
43541e413cf9SAndrew Gallatin 		return ENXIO;
43551e413cf9SAndrew Gallatin 	}
43561e413cf9SAndrew Gallatin 	if (mxge_verbose)
43571e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
435891ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
43591e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
43601e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
43611e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
436237d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
436337d89b0cSAndrew Gallatin 			     NULL,
436437d89b0cSAndrew Gallatin #endif
436537d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
43661e413cf9SAndrew Gallatin 	if (err != 0) {
43671e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
436891ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
436991ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
43701e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
43711e413cf9SAndrew Gallatin 	}
43721e413cf9SAndrew Gallatin 	return err;
43731e413cf9SAndrew Gallatin }
43741e413cf9SAndrew Gallatin 
43751e413cf9SAndrew Gallatin static void
43761e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
43771e413cf9SAndrew Gallatin {
43781e413cf9SAndrew Gallatin 	int i, rid;
43791e413cf9SAndrew Gallatin 
43801e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43811e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
43821e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43831e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
43841e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
43851e413cf9SAndrew Gallatin 		}
43861e413cf9SAndrew Gallatin 	}
43871e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
43881e413cf9SAndrew Gallatin 
43891e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43901e413cf9SAndrew Gallatin 		rid = i + 1;
43911e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
43921e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43931e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
43941e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
43951e413cf9SAndrew Gallatin 	}
43961e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43991e413cf9SAndrew Gallatin 			     sc->msix_table_res);
44001e413cf9SAndrew Gallatin 
44011e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
44021e413cf9SAndrew Gallatin 	return;
44031e413cf9SAndrew Gallatin }
44041e413cf9SAndrew Gallatin 
44051e413cf9SAndrew Gallatin static void
44061e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
44071e413cf9SAndrew Gallatin {
44081e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
44091e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
441091ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
441191ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
44121e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
44131e413cf9SAndrew Gallatin }
44141e413cf9SAndrew Gallatin 
44151e413cf9SAndrew Gallatin static void
44161e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
44171e413cf9SAndrew Gallatin {
44181e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44191e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44201e413cf9SAndrew Gallatin 	else
44211e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
44221e413cf9SAndrew Gallatin }
44231e413cf9SAndrew Gallatin 
44241e413cf9SAndrew Gallatin static int
44251e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
44261e413cf9SAndrew Gallatin {
44271e413cf9SAndrew Gallatin 	int err;
44281e413cf9SAndrew Gallatin 
44291e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44301e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44311e413cf9SAndrew Gallatin 	else
44321e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
44331e413cf9SAndrew Gallatin 
44341e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
44351e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44361e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44371e413cf9SAndrew Gallatin 	}
44381e413cf9SAndrew Gallatin 	return err;
44391e413cf9SAndrew Gallatin }
44401e413cf9SAndrew Gallatin 
4441b2fc195eSAndrew Gallatin 
4442b2fc195eSAndrew Gallatin static int
44436d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4444b2fc195eSAndrew Gallatin {
44456d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4446b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
44471e413cf9SAndrew Gallatin 	int err, rid;
4448b2fc195eSAndrew Gallatin 
4449b2fc195eSAndrew Gallatin 	sc->dev = dev;
44506d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4451b2fc195eSAndrew Gallatin 
4452b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4453b2fc195eSAndrew Gallatin 				 1,			/* alignment */
44541e413cf9SAndrew Gallatin 				 0,			/* boundary */
4455b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4456b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4457b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4458aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
44595e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
44601e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4461b2fc195eSAndrew Gallatin 				 0,			/* flags */
4462b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4463b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4464b2fc195eSAndrew Gallatin 
4465b2fc195eSAndrew Gallatin 	if (err != 0) {
4466b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4467b2fc195eSAndrew Gallatin 			      err);
4468b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4469b2fc195eSAndrew Gallatin 	}
4470b2fc195eSAndrew Gallatin 
4471b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4472b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4473b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4474b2fc195eSAndrew Gallatin 		err = ENOSPC;
4475b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4476b2fc195eSAndrew Gallatin 	}
44771e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
44781e413cf9SAndrew Gallatin 
4479a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4480a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4481a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4482a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4483a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4484a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4485b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4486b2fc195eSAndrew Gallatin 
4487dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4488d91b1b49SAndrew Gallatin 
4489dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4490b2fc195eSAndrew Gallatin 
4491b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4492b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4493b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4494b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4495b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4496b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4497b2fc195eSAndrew Gallatin 		err = ENXIO;
4498b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4499b2fc195eSAndrew Gallatin 	}
4500b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4501b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4502b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4503b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4504b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4505b2fc195eSAndrew Gallatin 		err = ENXIO;
4506b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4507b2fc195eSAndrew Gallatin 	}
4508b2fc195eSAndrew Gallatin 
4509b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4510b2fc195eSAndrew Gallatin 	   lanai SRAM */
45116d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4512b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4513b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
45146d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4515b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
45166d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
45176d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4518b2fc195eSAndrew Gallatin 	if (err != 0)
4519b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4520b2fc195eSAndrew Gallatin 
4521b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
45226d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4523b2fc195eSAndrew Gallatin 
4524b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
45256d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
45266d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4527b2fc195eSAndrew Gallatin 	if (err != 0)
4528b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4529b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
45306d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4531b2fc195eSAndrew Gallatin 	if (err != 0)
4532b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4533b2fc195eSAndrew Gallatin 
4534a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4535a98d6cd7SAndrew Gallatin 	if (err != 0)
45361e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4537b2fc195eSAndrew Gallatin 
45388fe615baSAndrew Gallatin 	/* select & load the firmware */
45398fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4540b2fc195eSAndrew Gallatin 	if (err != 0)
45411e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
45425e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
45431e413cf9SAndrew Gallatin 
45441e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
45451e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
45461e413cf9SAndrew Gallatin 	if (err != 0)
45471e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
45481e413cf9SAndrew Gallatin 
4549adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4550b2fc195eSAndrew Gallatin 	if (err != 0)
45511e413cf9SAndrew Gallatin 		goto abort_with_slices;
4552b2fc195eSAndrew Gallatin 
4553a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4554a98d6cd7SAndrew Gallatin 	if (err != 0) {
4555a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
45561e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4557a98d6cd7SAndrew Gallatin 	}
4558a98d6cd7SAndrew Gallatin 
45591e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4560a98d6cd7SAndrew Gallatin 	if (err != 0) {
45611e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4562a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4563a98d6cd7SAndrew Gallatin 	}
45641e413cf9SAndrew Gallatin 
4565e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4566c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
4567eb6219e3SAndrew Gallatin 		IFCAP_VLAN_MTU;
4568eb6219e3SAndrew Gallatin #ifdef INET
4569eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4570eb6219e3SAndrew Gallatin #endif
457137d89b0cSAndrew Gallatin 
457237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
457337d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
457437d89b0cSAndrew Gallatin #endif
4575c792928fSAndrew Gallatin 
4576053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4577053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4578053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4579053e637fSAndrew Gallatin 	else
4580053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4581adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4582053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4583aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4584b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4585f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4586f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
45875e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
45886d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4589b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4590b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
45916d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
45926d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4593c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4594c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4595c587e59fSAndrew Gallatin 		     mxge_media_status);
4596c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4597c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
45988c5d766cSAndrew Gallatin 	sc->dying = 0;
4599b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4600f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4601f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4602f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4603b2fc195eSAndrew Gallatin 
46046d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4605c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4606c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4607c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4608c6cb3e3fSAndrew Gallatin #endif
4609b2fc195eSAndrew Gallatin 	return 0;
4610b2fc195eSAndrew Gallatin 
4611a98d6cd7SAndrew Gallatin abort_with_rings:
4612a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
46131e413cf9SAndrew Gallatin abort_with_slices:
46141e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4615a98d6cd7SAndrew Gallatin abort_with_dmabench:
4616a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4617b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
46186d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4619b2fc195eSAndrew Gallatin abort_with_cmd_dma:
46206d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4621b2fc195eSAndrew Gallatin abort_with_mem_res:
4622b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4623b2fc195eSAndrew Gallatin abort_with_lock:
4624b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4625a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4626a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4627b2fc195eSAndrew Gallatin 	if_free(ifp);
4628b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4629b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4630b2fc195eSAndrew Gallatin 
4631b2fc195eSAndrew Gallatin abort_with_nothing:
4632b2fc195eSAndrew Gallatin 	return err;
4633b2fc195eSAndrew Gallatin }
4634b2fc195eSAndrew Gallatin 
4635b2fc195eSAndrew Gallatin static int
46366d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4637b2fc195eSAndrew Gallatin {
46386d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4639b2fc195eSAndrew Gallatin 
464037d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4641c792928fSAndrew Gallatin 		device_printf(sc->dev,
4642c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4643c792928fSAndrew Gallatin 		return EBUSY;
4644c792928fSAndrew Gallatin 	}
4645a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
46468c5d766cSAndrew Gallatin 	sc->dying = 1;
4647b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
46486d87a65dSAndrew Gallatin 		mxge_close(sc);
4649a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4650b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4651e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4652dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4653091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
46541e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
46551e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4656a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
46571e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4658a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
46596d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
46606d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4661b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4662b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4663a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4664a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4665b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4666b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4667b2fc195eSAndrew Gallatin 	return 0;
4668b2fc195eSAndrew Gallatin }
4669b2fc195eSAndrew Gallatin 
4670b2fc195eSAndrew Gallatin static int
46716d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4672b2fc195eSAndrew Gallatin {
4673b2fc195eSAndrew Gallatin 	return 0;
4674b2fc195eSAndrew Gallatin }
4675b2fc195eSAndrew Gallatin 
4676b2fc195eSAndrew Gallatin /*
4677b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4678b2fc195eSAndrew Gallatin 
4679b2fc195eSAndrew Gallatin   Local Variables:
4680b2fc195eSAndrew Gallatin   c-file-style:"linux"
4681b2fc195eSAndrew Gallatin   tab-width:8
4682b2fc195eSAndrew Gallatin   End:
4683b2fc195eSAndrew Gallatin */
4684