xref: /freebsd/sys/dev/mxge/if_mxge.c (revision bb8ddc66eab7ba2edf2f32e52d4945a873bbd978)
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;
1086d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1096d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1101e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1111e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
112b2fc195eSAndrew Gallatin 
1136d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1146d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1156d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1166d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1176d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
118b2fc195eSAndrew Gallatin 
1196d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
120b2fc195eSAndrew Gallatin {
121b2fc195eSAndrew Gallatin   /* Device interface */
1226d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1236d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1246d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1256d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
126b2fc195eSAndrew Gallatin   {0, 0}
127b2fc195eSAndrew Gallatin };
128b2fc195eSAndrew Gallatin 
1296d87a65dSAndrew Gallatin static driver_t mxge_driver =
130b2fc195eSAndrew Gallatin {
1316d87a65dSAndrew Gallatin   "mxge",
1326d87a65dSAndrew Gallatin   mxge_methods,
1336d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
134b2fc195eSAndrew Gallatin };
135b2fc195eSAndrew Gallatin 
1366d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
137b2fc195eSAndrew Gallatin 
138b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1396d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1406d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
141f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
142b2fc195eSAndrew Gallatin 
1431e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1448fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
145276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
146276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
147276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1488fe615baSAndrew Gallatin 
149b2fc195eSAndrew Gallatin static int
1506d87a65dSAndrew Gallatin mxge_probe(device_t dev)
151b2fc195eSAndrew Gallatin {
15201638550SAndrew Gallatin 	int rev;
15301638550SAndrew Gallatin 
15401638550SAndrew Gallatin 
1556d87a65dSAndrew Gallatin 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
156f1544498SAndrew Gallatin 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
157f1544498SAndrew Gallatin 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
15801638550SAndrew Gallatin 		rev = pci_get_revid(dev);
15901638550SAndrew Gallatin 		switch (rev) {
16001638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8E:
161b2fc195eSAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8A");
16201638550SAndrew Gallatin 			break;
16301638550SAndrew Gallatin 		case MXGE_PCI_REV_Z8ES:
16401638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8B");
16501638550SAndrew Gallatin 			break;
16601638550SAndrew Gallatin 		default:
16701638550SAndrew Gallatin 			device_set_desc(dev, "Myri10G-PCIE-8??");
16801638550SAndrew Gallatin 			device_printf(dev, "Unrecognized rev %d NIC\n",
16901638550SAndrew Gallatin 				      rev);
17001638550SAndrew Gallatin 			break;
17101638550SAndrew Gallatin 		}
172b2fc195eSAndrew Gallatin 		return 0;
173b2fc195eSAndrew Gallatin 	}
174b2fc195eSAndrew Gallatin 	return ENXIO;
175b2fc195eSAndrew Gallatin }
176b2fc195eSAndrew Gallatin 
177b2fc195eSAndrew Gallatin static void
1786d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
179b2fc195eSAndrew Gallatin {
180f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
181b2fc195eSAndrew Gallatin 	vm_offset_t len;
18247c2e987SAndrew Gallatin 	int err;
183b2fc195eSAndrew Gallatin 
1844d69a9d0SAndrew Gallatin 	sc->wc = 1;
185b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
186c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
187c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
18847c2e987SAndrew Gallatin 	if (err != 0) {
189c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
190c2c14a69SAndrew Gallatin 			      err);
1914d69a9d0SAndrew Gallatin 		sc->wc = 0;
192b2fc195eSAndrew Gallatin 	}
193f9ae0280SAndrew Gallatin #endif
194b2fc195eSAndrew Gallatin }
195b2fc195eSAndrew Gallatin 
196b2fc195eSAndrew Gallatin 
197b2fc195eSAndrew Gallatin /* callback to get our DMA address */
198b2fc195eSAndrew Gallatin static void
1996d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
200b2fc195eSAndrew Gallatin 			 int error)
201b2fc195eSAndrew Gallatin {
202b2fc195eSAndrew Gallatin 	if (error == 0) {
203b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
204b2fc195eSAndrew Gallatin 	}
205b2fc195eSAndrew Gallatin }
206b2fc195eSAndrew Gallatin 
207b2fc195eSAndrew Gallatin static int
2086d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
209b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
210b2fc195eSAndrew Gallatin {
211b2fc195eSAndrew Gallatin 	int err;
212b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2131e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2141e413cf9SAndrew Gallatin 
2151e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2161e413cf9SAndrew Gallatin 		boundary = 0;
2171e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2181e413cf9SAndrew Gallatin 	} else {
2191e413cf9SAndrew Gallatin 		boundary = 4096;
2201e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2211e413cf9SAndrew Gallatin 	}
222b2fc195eSAndrew Gallatin 
223b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
224b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
225b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2261e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
227b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
228b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
229b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
230b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
231b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2321e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
233b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
234b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
235b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
236b2fc195eSAndrew Gallatin 	if (err != 0) {
237b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
238b2fc195eSAndrew Gallatin 		return err;
239b2fc195eSAndrew Gallatin 	}
240b2fc195eSAndrew Gallatin 
241b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
242b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
243b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
244b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
245b2fc195eSAndrew Gallatin 	if (err != 0) {
246b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
247b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
248b2fc195eSAndrew Gallatin 	}
249b2fc195eSAndrew Gallatin 
250b2fc195eSAndrew Gallatin 	/* load the memory */
251b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2526d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
253b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
254b2fc195eSAndrew Gallatin 	if (err != 0) {
255b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
256b2fc195eSAndrew Gallatin 		goto abort_with_mem;
257b2fc195eSAndrew Gallatin 	}
258b2fc195eSAndrew Gallatin 	return 0;
259b2fc195eSAndrew Gallatin 
260b2fc195eSAndrew Gallatin abort_with_mem:
261b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
262b2fc195eSAndrew Gallatin abort_with_dmat:
263b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
264b2fc195eSAndrew Gallatin 	return err;
265b2fc195eSAndrew Gallatin }
266b2fc195eSAndrew Gallatin 
267b2fc195eSAndrew Gallatin 
268b2fc195eSAndrew Gallatin static void
2696d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
270b2fc195eSAndrew Gallatin {
271b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
272b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
273b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
274b2fc195eSAndrew Gallatin }
275b2fc195eSAndrew Gallatin 
276b2fc195eSAndrew Gallatin /*
277b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
278b2fc195eSAndrew Gallatin  * SN=x\0
279b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
280b2fc195eSAndrew Gallatin  * PC=text\0
281b2fc195eSAndrew Gallatin  */
282b2fc195eSAndrew Gallatin 
283b2fc195eSAndrew Gallatin static int
2846d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
285b2fc195eSAndrew Gallatin {
2866d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
287b2fc195eSAndrew Gallatin 
288b2fc195eSAndrew Gallatin 	char *ptr, *limit;
289b2fc195eSAndrew Gallatin 	int i, found_mac;
290b2fc195eSAndrew Gallatin 
291b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2926d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
293b2fc195eSAndrew Gallatin 	found_mac = 0;
294b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
295b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2965e7d8541SAndrew Gallatin 			ptr += 1;
297b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
298b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2995e7d8541SAndrew Gallatin 				ptr += 3;
300b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
301b2fc195eSAndrew Gallatin 					goto abort;
302b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
303b2fc195eSAndrew Gallatin 				found_mac = 1;
304b2fc195eSAndrew Gallatin 			}
3055e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
3065e7d8541SAndrew Gallatin 			ptr += 3;
3075e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3085e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3095e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3105e7d8541SAndrew Gallatin 			ptr += 3;
3115e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3125e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
313b2fc195eSAndrew Gallatin 		}
3146d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
315b2fc195eSAndrew Gallatin 	}
316b2fc195eSAndrew Gallatin 
317b2fc195eSAndrew Gallatin 	if (found_mac)
318b2fc195eSAndrew Gallatin 		return 0;
319b2fc195eSAndrew Gallatin 
320b2fc195eSAndrew Gallatin  abort:
321b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
322b2fc195eSAndrew Gallatin 
323b2fc195eSAndrew Gallatin 	return ENXIO;
324b2fc195eSAndrew Gallatin }
325b2fc195eSAndrew Gallatin 
3260d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3278fe615baSAndrew Gallatin static void
3288fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
329b2fc195eSAndrew Gallatin {
330b2fc195eSAndrew Gallatin 	uint32_t val;
3318fe615baSAndrew Gallatin 	unsigned long base, off;
332b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3338fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3348fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
335b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
336b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
337b2fc195eSAndrew Gallatin 
3388fe615baSAndrew Gallatin 
3398fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3408fe615baSAndrew Gallatin 		return;
3418fe615baSAndrew Gallatin 
3428fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3438fe615baSAndrew Gallatin 	if (pdev == NULL) {
3448fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3458fe615baSAndrew Gallatin 		return;
3468fe615baSAndrew Gallatin 	}
3478fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3488fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3498fe615baSAndrew Gallatin 
3508fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3518fe615baSAndrew Gallatin 		return;
3528fe615baSAndrew Gallatin 
3538fe615baSAndrew Gallatin 	base = 0;
3548fe615baSAndrew Gallatin 
3558fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3568fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3578fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3588fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3598fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3608fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3618fe615baSAndrew Gallatin 		if (mcp55 &&
3628fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3638fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3648fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3658fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3668fe615baSAndrew Gallatin 		}
3678fe615baSAndrew Gallatin 	}
3688fe615baSAndrew Gallatin 	if (!base)
3698fe615baSAndrew Gallatin 		return;
3708fe615baSAndrew Gallatin 
371b2fc195eSAndrew Gallatin 	/* XXXX
372b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
373b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
374b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
375b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
376b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
377b2fc195eSAndrew Gallatin 	*/
378b2fc195eSAndrew Gallatin #if 0
379b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
380b2fc195eSAndrew Gallatin 	   config space */
381b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
382b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
383b2fc195eSAndrew Gallatin 		val |= 0x40;
384b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3858fe615baSAndrew Gallatin 		return;
386b2fc195eSAndrew Gallatin 	}
387b2fc195eSAndrew Gallatin #endif
388b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
389b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
390b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
391b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
392b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
393b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
394b2fc195eSAndrew Gallatin 	 */
395b2fc195eSAndrew Gallatin 
396b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
397b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
398b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
399b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
400b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
401b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
402b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
403b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
404b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
405b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
406b2fc195eSAndrew Gallatin 
4078fe615baSAndrew Gallatin 	off =  base
408b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
409b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
410b2fc195eSAndrew Gallatin 						 + 8 * slot);
411b2fc195eSAndrew Gallatin 
412b2fc195eSAndrew Gallatin 	/* map it into the kernel */
413b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
414b2fc195eSAndrew Gallatin 
415b2fc195eSAndrew Gallatin 
416b2fc195eSAndrew Gallatin 	if (va == NULL) {
417b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4188fe615baSAndrew Gallatin 		return;
419b2fc195eSAndrew Gallatin 	}
420b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
421b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
422b2fc195eSAndrew Gallatin 
423b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
424b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
425b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
426b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
427b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
428b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
429b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4308fe615baSAndrew Gallatin 		return;
431b2fc195eSAndrew Gallatin 	}
432b2fc195eSAndrew Gallatin 
433b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
434b2fc195eSAndrew Gallatin 	val = *ptr32;
435b2fc195eSAndrew Gallatin 
436b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
437b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
438b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4398fe615baSAndrew Gallatin 		return;
440b2fc195eSAndrew Gallatin 	}
441b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
442b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4435e7d8541SAndrew Gallatin 	if (mxge_verbose)
444b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4455e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4465e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
447b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4488fe615baSAndrew Gallatin 	return;
449b2fc195eSAndrew Gallatin }
450b2fc195eSAndrew Gallatin #else
4518fe615baSAndrew Gallatin static void
452f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
453b2fc195eSAndrew Gallatin {
454b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
455b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4568fe615baSAndrew Gallatin 	return;
457b2fc195eSAndrew Gallatin }
458b2fc195eSAndrew Gallatin #endif
4598fe615baSAndrew Gallatin 
4608fe615baSAndrew Gallatin 
4618fe615baSAndrew Gallatin static int
4628fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4638fe615baSAndrew Gallatin {
4648fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4658fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4668fe615baSAndrew Gallatin 	int status;
4678fe615baSAndrew Gallatin 	uint32_t len;
4688fe615baSAndrew Gallatin 	char *test = " ";
4698fe615baSAndrew Gallatin 
4708fe615baSAndrew Gallatin 
4718fe615baSAndrew Gallatin 	/* Run a small DMA test.
4728fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4738fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4748fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4758fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4768fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4778fe615baSAndrew Gallatin 	 * transfers took to complete.
4788fe615baSAndrew Gallatin 	 */
4798fe615baSAndrew Gallatin 
4801e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4818fe615baSAndrew Gallatin 
4828fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4838fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4848fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4858fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4868fe615baSAndrew Gallatin 	if (status != 0) {
4878fe615baSAndrew Gallatin 		test = "read";
4888fe615baSAndrew Gallatin 		goto abort;
4898fe615baSAndrew Gallatin 	}
4908fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4918fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4928fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4938fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4948fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4958fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4968fe615baSAndrew Gallatin 	if (status != 0) {
4978fe615baSAndrew Gallatin 		test = "write";
4988fe615baSAndrew Gallatin 		goto abort;
4998fe615baSAndrew Gallatin 	}
5008fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5018fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5028fe615baSAndrew Gallatin 
5038fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5048fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5058fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5068fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5078fe615baSAndrew Gallatin 	if (status != 0) {
5088fe615baSAndrew Gallatin 		test = "read/write";
5098fe615baSAndrew Gallatin 		goto abort;
5108fe615baSAndrew Gallatin 	}
5118fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5128fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5138fe615baSAndrew Gallatin 
5148fe615baSAndrew Gallatin abort:
5158fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5168fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5178fe615baSAndrew Gallatin 			      test, status);
5188fe615baSAndrew Gallatin 
5198fe615baSAndrew Gallatin 	return status;
5208fe615baSAndrew Gallatin }
5218fe615baSAndrew Gallatin 
522b2fc195eSAndrew Gallatin /*
523b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
524b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
525b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
526b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
527b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
528b2fc195eSAndrew Gallatin  *
529b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
530b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
531b2fc195eSAndrew Gallatin  *
532b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
533b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
534b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
535b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5361e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
537b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5381e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
539b2fc195eSAndrew Gallatin  */
540b2fc195eSAndrew Gallatin 
5418fe615baSAndrew Gallatin static int
5428fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5438fe615baSAndrew Gallatin {
5448fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5458fe615baSAndrew Gallatin 	int reg, status;
5468fe615baSAndrew Gallatin 	uint16_t pectl;
5478fe615baSAndrew Gallatin 
5481e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5498fe615baSAndrew Gallatin 	/*
5508fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5518fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5528fe615baSAndrew Gallatin 	 */
5538fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5548fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5558fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5568fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5578fe615baSAndrew Gallatin 				      pectl);
5581e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5598fe615baSAndrew Gallatin 		}
5608fe615baSAndrew Gallatin 	}
5618fe615baSAndrew Gallatin 
5628fe615baSAndrew Gallatin 	/*
5638fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5648fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5658fe615baSAndrew Gallatin 	 */
5668fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5671e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5688fe615baSAndrew Gallatin 	if (status != 0) {
5698fe615baSAndrew Gallatin 		return status;
5708fe615baSAndrew Gallatin 	}
5718fe615baSAndrew Gallatin 
5728fe615baSAndrew Gallatin 	/*
5738fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5748fe615baSAndrew Gallatin 	 */
5758fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5768fe615baSAndrew Gallatin 
5778fe615baSAndrew Gallatin 	/*
5788fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5798fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5808fe615baSAndrew Gallatin 	 */
5818fe615baSAndrew Gallatin 
5828fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5838fe615baSAndrew Gallatin 	if (status == 0)
5848fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5858fe615baSAndrew Gallatin 
5868fe615baSAndrew Gallatin 	if (status != E2BIG)
5878fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5888fe615baSAndrew Gallatin 	if (status == ENOSYS)
5898fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5908fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5918fe615baSAndrew Gallatin 	return status;
5928fe615baSAndrew Gallatin }
5938fe615baSAndrew Gallatin 
5948fe615baSAndrew Gallatin static int
5956d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
596b2fc195eSAndrew Gallatin {
5978fe615baSAndrew Gallatin 	int aligned = 0;
598b2fc195eSAndrew Gallatin 
599d91b1b49SAndrew Gallatin 
600d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
601d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
602d91b1b49SAndrew Gallatin 			aligned = 1;
603d91b1b49SAndrew Gallatin 		else
604d91b1b49SAndrew Gallatin 			aligned = 0;
605d91b1b49SAndrew Gallatin 		if (mxge_verbose)
606d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
607d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
608d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
609d91b1b49SAndrew Gallatin 		goto abort;
610d91b1b49SAndrew Gallatin 	}
611d91b1b49SAndrew Gallatin 
612d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
613d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
614d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
615d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
616d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
617d91b1b49SAndrew Gallatin 			      sc->link_width);
618d91b1b49SAndrew Gallatin 		aligned = 1;
619d91b1b49SAndrew Gallatin 		goto abort;
620d91b1b49SAndrew Gallatin 	}
621d91b1b49SAndrew Gallatin 
6228fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6238fe615baSAndrew Gallatin 		return 0;
624b2fc195eSAndrew Gallatin 
625b2fc195eSAndrew Gallatin abort:
626b2fc195eSAndrew Gallatin 	if (aligned) {
6276d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6281e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
629b2fc195eSAndrew Gallatin 	} else {
6306d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6311e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
632b2fc195eSAndrew Gallatin 	}
6331e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
634b2fc195eSAndrew Gallatin }
635b2fc195eSAndrew Gallatin 
636b2fc195eSAndrew Gallatin union qualhack
637b2fc195eSAndrew Gallatin {
638b2fc195eSAndrew Gallatin         const char *ro_char;
639b2fc195eSAndrew Gallatin         char *rw_char;
640b2fc195eSAndrew Gallatin };
641b2fc195eSAndrew Gallatin 
6424da0d523SAndrew Gallatin static int
6434da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6444da0d523SAndrew Gallatin {
645b824b7d8SAndrew Gallatin 
6464da0d523SAndrew Gallatin 
6474da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6484da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6494da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6504da0d523SAndrew Gallatin 		return EIO;
6514da0d523SAndrew Gallatin 	}
6524da0d523SAndrew Gallatin 
6534da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6544da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6554da0d523SAndrew Gallatin 	if (mxge_verbose)
6564da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6574da0d523SAndrew Gallatin 
658b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
659b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6604da0d523SAndrew Gallatin 
661b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
662b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6634da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6644da0d523SAndrew Gallatin 			      sc->fw_version);
6654da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6664da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6674da0d523SAndrew Gallatin 		return EINVAL;
6684da0d523SAndrew Gallatin 	}
6694da0d523SAndrew Gallatin 	return 0;
6704da0d523SAndrew Gallatin 
6714da0d523SAndrew Gallatin }
672b2fc195eSAndrew Gallatin 
673f9ae0280SAndrew Gallatin static void *
674f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
675f9ae0280SAndrew Gallatin {
676f9ae0280SAndrew Gallatin         void *ptr;
677f9ae0280SAndrew Gallatin 
678f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
679f9ae0280SAndrew Gallatin         return ptr;
680f9ae0280SAndrew Gallatin }
681f9ae0280SAndrew Gallatin 
682f9ae0280SAndrew Gallatin static void
683f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
684f9ae0280SAndrew Gallatin {
685f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
686f9ae0280SAndrew Gallatin }
687f9ae0280SAndrew Gallatin 
688f9ae0280SAndrew Gallatin 
689b2fc195eSAndrew Gallatin static int
6906d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
691b2fc195eSAndrew Gallatin {
692f9ae0280SAndrew Gallatin 	z_stream zs;
693f9ae0280SAndrew Gallatin 	char *inflate_buffer;
69433d54970SLuigi Rizzo 	const struct firmware *fw;
695b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
696b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
697b2fc195eSAndrew Gallatin 	int status;
6984da0d523SAndrew Gallatin 	unsigned int i;
6994da0d523SAndrew Gallatin 	char dummy;
700f9ae0280SAndrew Gallatin 	size_t fw_len;
701b2fc195eSAndrew Gallatin 
702b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
703b2fc195eSAndrew Gallatin 	if (fw == NULL) {
704b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
705b2fc195eSAndrew Gallatin 			      sc->fw_name);
706b2fc195eSAndrew Gallatin 		return ENOENT;
707b2fc195eSAndrew Gallatin 	}
708b2fc195eSAndrew Gallatin 
709f9ae0280SAndrew Gallatin 
710f9ae0280SAndrew Gallatin 
711f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
712f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
713f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
714f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
715f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
716f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
717b2fc195eSAndrew Gallatin 		status = EIO;
718b2fc195eSAndrew Gallatin 		goto abort_with_fw;
719b2fc195eSAndrew Gallatin 	}
720f9ae0280SAndrew Gallatin 
721f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
722f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
723f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
724f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
725f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
726f9ae0280SAndrew Gallatin 		goto abort_with_zs;
727f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
728f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
729f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
730f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
731f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
732f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
733f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
734f9ae0280SAndrew Gallatin 		status = EIO;
735f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
736f9ae0280SAndrew Gallatin 	}
737f9ae0280SAndrew Gallatin 
738f9ae0280SAndrew Gallatin 	/* check id */
739f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
740f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
741f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
742f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
743f9ae0280SAndrew Gallatin 		status = EIO;
744f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
745f9ae0280SAndrew Gallatin 	}
746f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
747b2fc195eSAndrew Gallatin 
7484da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7494da0d523SAndrew Gallatin 	if (status != 0)
750f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
751b2fc195eSAndrew Gallatin 
752b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
753f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7544da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
755f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
756f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
75773c7c83fSAndrew Gallatin 		wmb();
7584da0d523SAndrew Gallatin 		dummy = *sc->sram;
75973c7c83fSAndrew Gallatin 		wmb();
7604da0d523SAndrew Gallatin 	}
761b2fc195eSAndrew Gallatin 
762f9ae0280SAndrew Gallatin 	*limit = fw_len;
763b2fc195eSAndrew Gallatin 	status = 0;
764f9ae0280SAndrew Gallatin abort_with_buffer:
765f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
766f9ae0280SAndrew Gallatin abort_with_zs:
767f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
768b2fc195eSAndrew Gallatin abort_with_fw:
769b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
770b2fc195eSAndrew Gallatin 	return status;
771b2fc195eSAndrew Gallatin }
772b2fc195eSAndrew Gallatin 
773b2fc195eSAndrew Gallatin /*
774b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
775b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
776b2fc195eSAndrew Gallatin  */
777b2fc195eSAndrew Gallatin 
778b2fc195eSAndrew Gallatin static void
7796d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
780b2fc195eSAndrew Gallatin {
781b2fc195eSAndrew Gallatin 	char buf_bytes[72];
782b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
783b2fc195eSAndrew Gallatin 	volatile char *submit;
784b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
785b2fc195eSAndrew Gallatin 	int i;
786b2fc195eSAndrew Gallatin 
787b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
788b2fc195eSAndrew Gallatin 
789b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
790b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
791b2fc195eSAndrew Gallatin 	*confirm = 0;
79273c7c83fSAndrew Gallatin 	wmb();
793b2fc195eSAndrew Gallatin 
794b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
795b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
796b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
797b2fc195eSAndrew Gallatin 	*/
798b2fc195eSAndrew Gallatin 
7996d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8006d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
801b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
802b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
803b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8046d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8056d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
806b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
807b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
808b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
809b2fc195eSAndrew Gallatin 
810b2fc195eSAndrew Gallatin 
8110fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
812b2fc195eSAndrew Gallatin 
8136d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
81473c7c83fSAndrew Gallatin 	wmb();
815b2fc195eSAndrew Gallatin 	DELAY(1000);
81673c7c83fSAndrew Gallatin 	wmb();
817b2fc195eSAndrew Gallatin 	i = 0;
818b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
819b2fc195eSAndrew Gallatin 		DELAY(1000);
820b2fc195eSAndrew Gallatin 		i++;
821b2fc195eSAndrew Gallatin 	}
822b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
823b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
824b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
825b2fc195eSAndrew Gallatin 			      *confirm);
826b2fc195eSAndrew Gallatin 	}
827b2fc195eSAndrew Gallatin 	return;
828b2fc195eSAndrew Gallatin }
829b2fc195eSAndrew Gallatin 
830b2fc195eSAndrew Gallatin static int
8316d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
832b2fc195eSAndrew Gallatin {
833b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
834b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
835b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8360fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
837b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
838e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
839b2fc195eSAndrew Gallatin 
840b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
841b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
842b2fc195eSAndrew Gallatin 
843b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
844b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
845b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
846b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8476d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8486d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
849b2fc195eSAndrew Gallatin 
850b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
851b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
852a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
853b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
85473c7c83fSAndrew Gallatin 	wmb();
8556d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
856b2fc195eSAndrew Gallatin 
8575e7d8541SAndrew Gallatin 	/* wait up to 20ms */
858e0501fd0SAndrew Gallatin 	err = EAGAIN;
8595e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
860b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
861b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
86273c7c83fSAndrew Gallatin 		wmb();
863e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
864e0501fd0SAndrew Gallatin 		case 0:
865b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
866e0501fd0SAndrew Gallatin 			err = 0;
867e0501fd0SAndrew Gallatin 			break;
868e0501fd0SAndrew Gallatin 		case 0xffffffff:
869e0501fd0SAndrew Gallatin 			DELAY(1000);
870e0501fd0SAndrew Gallatin 			break;
871e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
872e0501fd0SAndrew Gallatin 			err = ENOSYS;
873e0501fd0SAndrew Gallatin 			break;
874e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
875e0501fd0SAndrew Gallatin 			err = E2BIG;
876e0501fd0SAndrew Gallatin 			break;
877c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
878c587e59fSAndrew Gallatin 			err = EBUSY;
879c587e59fSAndrew Gallatin 			break;
880e0501fd0SAndrew Gallatin 		default:
881b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8826d87a65dSAndrew Gallatin 				      "mxge: command %d "
883b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
884b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
885e0501fd0SAndrew Gallatin 			err = ENXIO;
886e0501fd0SAndrew Gallatin 			break;
887b2fc195eSAndrew Gallatin 		}
888e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
889e0501fd0SAndrew Gallatin 			break;
890b2fc195eSAndrew Gallatin 	}
891e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8926d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
893b2fc195eSAndrew Gallatin 			      "result = %d\n",
894b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
895e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
896e0501fd0SAndrew Gallatin 	return err;
897b2fc195eSAndrew Gallatin }
898b2fc195eSAndrew Gallatin 
8994da0d523SAndrew Gallatin static int
9004da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
9014da0d523SAndrew Gallatin {
9024da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
9034da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
9044da0d523SAndrew Gallatin 	size_t hdr_offset;
9054da0d523SAndrew Gallatin 	int status;
9064da0d523SAndrew Gallatin 
9074da0d523SAndrew Gallatin 	/* find running firmware header */
9084da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9094da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9104da0d523SAndrew Gallatin 
9114da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9124da0d523SAndrew Gallatin 		device_printf(sc->dev,
9134da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9144da0d523SAndrew Gallatin 			      (int)hdr_offset);
9154da0d523SAndrew Gallatin 		return EIO;
9164da0d523SAndrew Gallatin 	}
9174da0d523SAndrew Gallatin 
9184da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9194da0d523SAndrew Gallatin 	 * validate firmware */
9204da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9214da0d523SAndrew Gallatin 	if (hdr == NULL) {
9224da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9234da0d523SAndrew Gallatin 		return ENOMEM;
9244da0d523SAndrew Gallatin 	}
9254da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9264da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9274da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9284da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9294da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
930b824b7d8SAndrew Gallatin 
931b824b7d8SAndrew Gallatin 	/*
932b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
933b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
934b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
935b824b7d8SAndrew Gallatin 	 */
936b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
937b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
938b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
939b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
940b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
941b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
942b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
943b824b7d8SAndrew Gallatin 	}
944b824b7d8SAndrew Gallatin 
9454da0d523SAndrew Gallatin 	return status;
9464da0d523SAndrew Gallatin }
9474da0d523SAndrew Gallatin 
948b2fc195eSAndrew Gallatin 
949b2fc195eSAndrew Gallatin static int
9501e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
951b2fc195eSAndrew Gallatin {
952b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
953b2fc195eSAndrew Gallatin 	volatile char *submit;
954b2fc195eSAndrew Gallatin 	char buf_bytes[72];
955b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
956b2fc195eSAndrew Gallatin 	int status, i;
957b2fc195eSAndrew Gallatin 
958b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
959b2fc195eSAndrew Gallatin 
960b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9616d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
962b2fc195eSAndrew Gallatin 	if (status) {
9631e413cf9SAndrew Gallatin 		if (!adopt)
9641e413cf9SAndrew Gallatin 			return status;
9654da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9664da0d523SAndrew Gallatin 		   it is new enough */
9674da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9684da0d523SAndrew Gallatin 		if (status) {
9694da0d523SAndrew Gallatin 			device_printf(sc->dev,
9704da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
971b2fc195eSAndrew Gallatin 			return status;
972b2fc195eSAndrew Gallatin 		}
9734da0d523SAndrew Gallatin 		device_printf(sc->dev,
9744da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9751e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9764da0d523SAndrew Gallatin 			device_printf(sc->dev,
9774da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9784da0d523SAndrew Gallatin 				 ".  For optimal\n");
9794da0d523SAndrew Gallatin 			device_printf(sc->dev,
9804da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9814da0d523SAndrew Gallatin 				 "firmware\n");
9824da0d523SAndrew Gallatin 		}
983d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9841e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
985d91b1b49SAndrew Gallatin 		return 0;
9864da0d523SAndrew Gallatin 	}
987b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
988b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
989b2fc195eSAndrew Gallatin 	*confirm = 0;
99073c7c83fSAndrew Gallatin 	wmb();
991b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
992b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
993b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
994b2fc195eSAndrew Gallatin 	*/
995b2fc195eSAndrew Gallatin 
9966d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9976d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
998b2fc195eSAndrew Gallatin 
999b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
1000b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
1001b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
1002b2fc195eSAndrew Gallatin 
1003b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
1004b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
1005b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
1006b2fc195eSAndrew Gallatin 	*/
1007b2fc195eSAndrew Gallatin 					/* where the code starts*/
10086d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1009b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1010b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1011b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1012b2fc195eSAndrew Gallatin 
10130fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10146d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
101573c7c83fSAndrew Gallatin 	wmb();
1016b2fc195eSAndrew Gallatin 	DELAY(1000);
101773c7c83fSAndrew Gallatin 	wmb();
1018b2fc195eSAndrew Gallatin 	i = 0;
1019b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1020b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1021b2fc195eSAndrew Gallatin 		i++;
1022b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1023b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1024b2fc195eSAndrew Gallatin 	}
1025b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1026b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1027b2fc195eSAndrew Gallatin 			confirm, *confirm);
1028b2fc195eSAndrew Gallatin 
1029b2fc195eSAndrew Gallatin 		return ENXIO;
1030b2fc195eSAndrew Gallatin 	}
1031b2fc195eSAndrew Gallatin 	return 0;
1032b2fc195eSAndrew Gallatin }
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin static int
10356d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1036b2fc195eSAndrew Gallatin {
10376d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1038b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1039b2fc195eSAndrew Gallatin 	int status;
1040b2fc195eSAndrew Gallatin 
1041b2fc195eSAndrew Gallatin 
1042b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1043b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1044b2fc195eSAndrew Gallatin 
1045b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1046b2fc195eSAndrew Gallatin 
10475e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1048b2fc195eSAndrew Gallatin 	return status;
1049b2fc195eSAndrew Gallatin }
1050b2fc195eSAndrew Gallatin 
1051b2fc195eSAndrew Gallatin static int
10526d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1053b2fc195eSAndrew Gallatin {
10546d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1055b2fc195eSAndrew Gallatin 	int status;
1056b2fc195eSAndrew Gallatin 
1057b2fc195eSAndrew Gallatin 	if (pause)
10585e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1059b2fc195eSAndrew Gallatin 				       &cmd);
1060b2fc195eSAndrew Gallatin 	else
10615e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1062b2fc195eSAndrew Gallatin 				       &cmd);
1063b2fc195eSAndrew Gallatin 
1064b2fc195eSAndrew Gallatin 	if (status) {
1065b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1066b2fc195eSAndrew Gallatin 		return ENXIO;
1067b2fc195eSAndrew Gallatin 	}
1068b2fc195eSAndrew Gallatin 	sc->pause = pause;
1069b2fc195eSAndrew Gallatin 	return 0;
1070b2fc195eSAndrew Gallatin }
1071b2fc195eSAndrew Gallatin 
1072b2fc195eSAndrew Gallatin static void
10736d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1074b2fc195eSAndrew Gallatin {
10756d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1076b2fc195eSAndrew Gallatin 	int status;
1077b2fc195eSAndrew Gallatin 
10781e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10791e413cf9SAndrew Gallatin 		promisc = 1;
10801e413cf9SAndrew Gallatin 
1081b2fc195eSAndrew Gallatin 	if (promisc)
10825e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1083b2fc195eSAndrew Gallatin 				       &cmd);
1084b2fc195eSAndrew Gallatin 	else
10855e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1086b2fc195eSAndrew Gallatin 				       &cmd);
1087b2fc195eSAndrew Gallatin 
1088b2fc195eSAndrew Gallatin 	if (status) {
1089b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1090b2fc195eSAndrew Gallatin 	}
1091b2fc195eSAndrew Gallatin }
1092b2fc195eSAndrew Gallatin 
10930fa7f681SAndrew Gallatin static void
10940fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10950fa7f681SAndrew Gallatin {
10960fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10970fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10980fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10990fa7f681SAndrew Gallatin 	int err;
11000fa7f681SAndrew Gallatin 
11010fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11020fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11030fa7f681SAndrew Gallatin 		return;
11040fa7f681SAndrew Gallatin 
11050fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11060fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11070fa7f681SAndrew Gallatin 	if (err != 0) {
11080fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11090fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11100fa7f681SAndrew Gallatin 		return;
11110fa7f681SAndrew Gallatin 	}
11120fa7f681SAndrew Gallatin 
1113b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1114b824b7d8SAndrew Gallatin 		return;
11150fa7f681SAndrew Gallatin 
11160fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11170fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11180fa7f681SAndrew Gallatin 		return;
11190fa7f681SAndrew Gallatin 
11200fa7f681SAndrew Gallatin 	/* Flush all the filters */
11210fa7f681SAndrew Gallatin 
11220fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11230fa7f681SAndrew Gallatin 	if (err != 0) {
11240fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11250fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11260fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11270fa7f681SAndrew Gallatin 		return;
11280fa7f681SAndrew Gallatin 	}
11290fa7f681SAndrew Gallatin 
11300fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11310fa7f681SAndrew Gallatin 
11320fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
11330fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11340fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11350fa7f681SAndrew Gallatin 			continue;
11360fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11370fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11380fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11390fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11400fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11410fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11420fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11430fa7f681SAndrew Gallatin 		if (err != 0) {
11440fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11450fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11460fa7f681SAndrew Gallatin 			       "%d\t", err);
11470fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
11480fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
11490fa7f681SAndrew Gallatin 			return;
11500fa7f681SAndrew Gallatin 		}
11510fa7f681SAndrew Gallatin 	}
11520fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
11530fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11540fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11550fa7f681SAndrew Gallatin 	if (err != 0) {
11560fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11570fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11580fa7f681SAndrew Gallatin 	}
11590fa7f681SAndrew Gallatin }
11600fa7f681SAndrew Gallatin 
1161b2fc195eSAndrew Gallatin static int
1162053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1163053e637fSAndrew Gallatin {
1164053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1165053e637fSAndrew Gallatin 	int status;
1166053e637fSAndrew Gallatin 
1167c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1168c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1169053e637fSAndrew Gallatin 
1170053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1171053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1172053e637fSAndrew Gallatin 	cmd.data0 = 0;
1173053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1174053e637fSAndrew Gallatin 			       &cmd);
1175053e637fSAndrew Gallatin 	if (status == 0)
1176c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1177053e637fSAndrew Gallatin 
1178053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1179053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1180053e637fSAndrew Gallatin }
1181053e637fSAndrew Gallatin 
1182053e637fSAndrew Gallatin static int
1183adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1184b2fc195eSAndrew Gallatin {
11851e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11861e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11871e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11886d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11891e413cf9SAndrew Gallatin 	int slice, status;
1190b2fc195eSAndrew Gallatin 
1191b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1192b2fc195eSAndrew Gallatin 	   is alive */
1193b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11945e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1195b2fc195eSAndrew Gallatin 	if (status != 0) {
1196b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1197b2fc195eSAndrew Gallatin 		return ENXIO;
1198b2fc195eSAndrew Gallatin 	}
1199b2fc195eSAndrew Gallatin 
1200091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1201091feecdSAndrew Gallatin 
12021e413cf9SAndrew Gallatin 
12031e413cf9SAndrew Gallatin 	/* set the intrq size */
12041e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
12051e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12061e413cf9SAndrew Gallatin 
12071e413cf9SAndrew Gallatin 	/*
12081e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12091e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12101e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12111e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12121e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12131e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12141e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12151e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12161e413cf9SAndrew Gallatin 	 */
12171e413cf9SAndrew Gallatin 
12181e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12191e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12201e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12211e413cf9SAndrew Gallatin 					   &cmd);
12221e413cf9SAndrew Gallatin 		if (status != 0) {
12231e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12241e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12251e413cf9SAndrew Gallatin 			return status;
12261e413cf9SAndrew Gallatin 		}
12271e413cf9SAndrew Gallatin 		/*
12281e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12291e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12301e413cf9SAndrew Gallatin 		 */
12311e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12321e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1233c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1234c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
1235c6cb3e3fSAndrew Gallatin #endif
12361e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12371e413cf9SAndrew Gallatin 					   &cmd);
12381e413cf9SAndrew Gallatin 		if (status != 0) {
12391e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12401e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12411e413cf9SAndrew Gallatin 			return status;
12421e413cf9SAndrew Gallatin 		}
12431e413cf9SAndrew Gallatin 	}
12441e413cf9SAndrew Gallatin 
12451e413cf9SAndrew Gallatin 
1246adae7080SAndrew Gallatin 	if (interrupts_setup) {
1247b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12481e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12491e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12501e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12511e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12521e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12531e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12541e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12551e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12561e413cf9SAndrew Gallatin 						&cmd);
12571e413cf9SAndrew Gallatin 		}
1258adae7080SAndrew Gallatin 	}
1259b2fc195eSAndrew Gallatin 
12606d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12615e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12625e7d8541SAndrew Gallatin 
12635e7d8541SAndrew Gallatin 
12645e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12655e7d8541SAndrew Gallatin 
12665e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12671e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12685e7d8541SAndrew Gallatin 
12695e7d8541SAndrew Gallatin 
12705e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12716d87a65dSAndrew Gallatin 				&cmd);
12725e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1273b2fc195eSAndrew Gallatin 	if (status != 0) {
1274b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1275b2fc195eSAndrew Gallatin 		return status;
1276b2fc195eSAndrew Gallatin 	}
1277b2fc195eSAndrew Gallatin 
12785e7d8541SAndrew Gallatin 
12795e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12805e7d8541SAndrew Gallatin 
12815e7d8541SAndrew Gallatin 
12825e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12838fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12845e7d8541SAndrew Gallatin 
12851e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12861e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12871e413cf9SAndrew Gallatin 
12881e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1289b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12901e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12911e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12921e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12931e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12941e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1295c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1296c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1297c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12981e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12991e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
13001e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
13011e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
13021e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
13031e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
13041e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
13051e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
13061e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
13071e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
13081e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
13091e413cf9SAndrew Gallatin 		}
13101e413cf9SAndrew Gallatin 	}
1311b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
13126d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
1313bb8ddc66SAndrew Gallatin 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13146d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13150fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1316b2fc195eSAndrew Gallatin 	return status;
1317b2fc195eSAndrew Gallatin }
1318b2fc195eSAndrew Gallatin 
1319b2fc195eSAndrew Gallatin static int
13206d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1321b2fc195eSAndrew Gallatin {
13226d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1323b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1324b2fc195eSAndrew Gallatin         int err;
1325b2fc195eSAndrew Gallatin 
1326b2fc195eSAndrew Gallatin         sc = arg1;
1327b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1328b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1329b2fc195eSAndrew Gallatin         if (err != 0) {
1330b2fc195eSAndrew Gallatin                 return err;
1331b2fc195eSAndrew Gallatin         }
1332b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1333b2fc195eSAndrew Gallatin                 return 0;
1334b2fc195eSAndrew Gallatin 
1335b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1336b2fc195eSAndrew Gallatin                 return EINVAL;
1337b2fc195eSAndrew Gallatin 
1338a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13395e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1340b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13415e7d8541SAndrew Gallatin 
1342a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1343b2fc195eSAndrew Gallatin         return err;
1344b2fc195eSAndrew Gallatin }
1345b2fc195eSAndrew Gallatin 
1346b2fc195eSAndrew Gallatin static int
13476d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1348b2fc195eSAndrew Gallatin {
13496d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1350b2fc195eSAndrew Gallatin         unsigned int enabled;
1351b2fc195eSAndrew Gallatin         int err;
1352b2fc195eSAndrew Gallatin 
1353b2fc195eSAndrew Gallatin         sc = arg1;
1354b2fc195eSAndrew Gallatin         enabled = sc->pause;
1355b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1356b2fc195eSAndrew Gallatin         if (err != 0) {
1357b2fc195eSAndrew Gallatin                 return err;
1358b2fc195eSAndrew Gallatin         }
1359b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1360b2fc195eSAndrew Gallatin                 return 0;
1361b2fc195eSAndrew Gallatin 
1362a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13636d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1364a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1365b2fc195eSAndrew Gallatin         return err;
1366b2fc195eSAndrew Gallatin }
1367b2fc195eSAndrew Gallatin 
1368b2fc195eSAndrew Gallatin static int
1369f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1370f04b33f8SAndrew Gallatin {
1371f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1372c587e59fSAndrew Gallatin 	int err = 0;
1373f04b33f8SAndrew Gallatin 
1374f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1375f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1376f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1377f04b33f8SAndrew Gallatin 	else
1378f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1379f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1380c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1381f04b33f8SAndrew Gallatin 		mxge_close(sc);
1382f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1383c587e59fSAndrew Gallatin 	}
1384f04b33f8SAndrew Gallatin 	return err;
1385f04b33f8SAndrew Gallatin }
1386f04b33f8SAndrew Gallatin 
1387f04b33f8SAndrew Gallatin static int
1388276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1389276edd10SAndrew Gallatin {
1390276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1391276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1392276edd10SAndrew Gallatin 	int err;
1393276edd10SAndrew Gallatin 
1394276edd10SAndrew Gallatin 	sc = arg1;
1395276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1396276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1397276edd10SAndrew Gallatin 	if (err != 0)
1398276edd10SAndrew Gallatin 		return err;
1399276edd10SAndrew Gallatin 
1400276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1401276edd10SAndrew Gallatin 		return 0;
1402276edd10SAndrew Gallatin 
1403276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1404276edd10SAndrew Gallatin 		return EINVAL;
1405276edd10SAndrew Gallatin 
1406276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1407f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1408276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1409276edd10SAndrew Gallatin 	return err;
1410276edd10SAndrew Gallatin }
1411276edd10SAndrew Gallatin 
1412276edd10SAndrew Gallatin static int
14136d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1414b2fc195eSAndrew Gallatin {
1415b2fc195eSAndrew Gallatin         int err;
1416b2fc195eSAndrew Gallatin 
1417b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1418b2fc195eSAndrew Gallatin                 return EFAULT;
1419b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1420b2fc195eSAndrew Gallatin         arg1 = NULL;
1421b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1422b2fc195eSAndrew Gallatin 
1423b2fc195eSAndrew Gallatin         return err;
1424b2fc195eSAndrew Gallatin }
1425b2fc195eSAndrew Gallatin 
1426b2fc195eSAndrew Gallatin static void
14271e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14281e413cf9SAndrew Gallatin {
14291e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14301e413cf9SAndrew Gallatin 	int slice;
14311e413cf9SAndrew Gallatin 
14321e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14331e413cf9SAndrew Gallatin 		return;
14341e413cf9SAndrew Gallatin 
14351e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14361e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14371e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14381e413cf9SAndrew Gallatin 			continue;
14391e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14401e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14411e413cf9SAndrew Gallatin 	}
14421e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14431e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14441e413cf9SAndrew Gallatin }
14451e413cf9SAndrew Gallatin 
14461e413cf9SAndrew Gallatin static void
14476d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1448b2fc195eSAndrew Gallatin {
1449b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1450b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14515e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14521e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14531e413cf9SAndrew Gallatin 	int slice;
14541e413cf9SAndrew Gallatin 	char slice_num[8];
1455b2fc195eSAndrew Gallatin 
1456b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1457b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14581e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1459b2fc195eSAndrew Gallatin 
14605e7d8541SAndrew Gallatin 	/* random information */
14615e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14625e7d8541SAndrew Gallatin 		       "firmware_version",
14635e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
14645e7d8541SAndrew Gallatin 		       0, "firmware version");
14655e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14665e7d8541SAndrew Gallatin 		       "serial_number",
14675e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
14685e7d8541SAndrew Gallatin 		       0, "serial number");
14695e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14705e7d8541SAndrew Gallatin 		       "product_code",
14715e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
14725e7d8541SAndrew Gallatin 		       0, "product_code");
14735e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1474d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1475d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1476d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1477d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14785e7d8541SAndrew Gallatin 		       "tx_boundary",
14791e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14805e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14815e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1482091feecdSAndrew Gallatin 		       "write_combine",
1483091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1484091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1485091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14865e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14875e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14885e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14895e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14905e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14915e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14925e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14935e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14945e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14955e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14965e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
14975e7d8541SAndrew Gallatin 
14985e7d8541SAndrew Gallatin 
14995e7d8541SAndrew Gallatin 	/* performance related tunables */
1500b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1501b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1502b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15036d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1504b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1505b2fc195eSAndrew Gallatin 
1506b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1507b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1508b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
15096d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1510b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1511b2fc195eSAndrew Gallatin 
1512b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15135e7d8541SAndrew Gallatin 		       "deassert_wait",
15145e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15155e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1516b2fc195eSAndrew Gallatin 
1517b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1518b2fc195eSAndrew Gallatin 	   Need to swap it */
1519b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1520b2fc195eSAndrew Gallatin 			"link_up",
1521b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15226d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1523b2fc195eSAndrew Gallatin 			"I", "link up");
1524b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1525b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1526b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15276d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1528b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1529b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1530adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1531adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1532adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15336d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1534adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1535adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1536adae7080SAndrew Gallatin 			"dropped_bad_phy",
1537adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1538adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1539adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1540adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1541b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1542b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1543b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1544b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15456d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1546b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1547b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1548adae7080SAndrew Gallatin 			"dropped_link_overflow",
1549adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1550adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1551adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1552adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15530fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15540fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15550fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15560fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15570fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15580fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1559adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1560adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15616d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1562adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1563b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1564b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1565b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1566b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15676d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1568b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1569b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1570adae7080SAndrew Gallatin 			"dropped_overrun",
1571adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15726d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1573adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1574adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1575adae7080SAndrew Gallatin 			"dropped_pause",
1576adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1577adae7080SAndrew Gallatin 			&fw->dropped_pause,
1578adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1579adae7080SAndrew Gallatin 			"I", "dropped_pause");
1580adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1581adae7080SAndrew Gallatin 			"dropped_runt",
1582adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1583adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1584adae7080SAndrew Gallatin 			"I", "dropped_runt");
1585b2fc195eSAndrew Gallatin 
1586a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1587a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1588a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1589a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1590a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1591a0394e33SAndrew Gallatin 
15925e7d8541SAndrew Gallatin 	/* verbose printing? */
1593b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15945e7d8541SAndrew Gallatin 		       "verbose",
15955e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15965e7d8541SAndrew Gallatin 		       0, "verbose printing");
1597b2fc195eSAndrew Gallatin 
1598053e637fSAndrew Gallatin 	/* lro */
1599276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1600276edd10SAndrew Gallatin 			"lro_cnt",
1601276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1602276edd10SAndrew Gallatin 			0, mxge_change_lro,
1603276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1604053e637fSAndrew Gallatin 
16051e413cf9SAndrew Gallatin 
16061e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
16071e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16081e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
16091e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16101e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16111e413cf9SAndrew Gallatin 
16121e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16131e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16141e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16151e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16161e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16171e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16181e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16191e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16201e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16211e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1622053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16231e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16241e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16251e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16261e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16271e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16281e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16291e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16301e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16311e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1632053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1633053e637fSAndrew Gallatin 
1634053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16351e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16361e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16371e413cf9SAndrew Gallatin 			       "queues");
1638053e637fSAndrew Gallatin 
1639c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
16401e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16411e413cf9SAndrew Gallatin 		if (slice > 0)
16421e413cf9SAndrew Gallatin 			continue;
1643c6cb3e3fSAndrew Gallatin #endif
1644c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1645c6cb3e3fSAndrew Gallatin 			       "tx_req",
1646c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1647c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16481e413cf9SAndrew Gallatin 
16491e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16501e413cf9SAndrew Gallatin 			       "tx_done",
16511e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16521e413cf9SAndrew Gallatin 			       0, "tx_done");
16531e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16541e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16551e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16561e413cf9SAndrew Gallatin 			       0, "tx_done");
16571e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16581e413cf9SAndrew Gallatin 			       "tx_stall",
16591e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16601e413cf9SAndrew Gallatin 			       0, "tx_stall");
16611e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16621e413cf9SAndrew Gallatin 			       "tx_wake",
16631e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16641e413cf9SAndrew Gallatin 			       0, "tx_wake");
16651e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16661e413cf9SAndrew Gallatin 			       "tx_defrag",
16671e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16681e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1669c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1670c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1671c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1672c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1673c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1674c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1675c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1676c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1677c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1678c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1679c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1680c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16811e413cf9SAndrew Gallatin 	}
1682b2fc195eSAndrew Gallatin }
1683b2fc195eSAndrew Gallatin 
1684b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1685b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1686b2fc195eSAndrew Gallatin 
1687b2fc195eSAndrew Gallatin static inline void
16881e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1689b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1690b2fc195eSAndrew Gallatin {
1691b2fc195eSAndrew Gallatin         int idx, starting_slot;
1692b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1693b2fc195eSAndrew Gallatin         while (cnt > 1) {
1694b2fc195eSAndrew Gallatin                 cnt--;
1695b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
16966d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1697b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
169873c7c83fSAndrew Gallatin                 wmb();
1699b2fc195eSAndrew Gallatin         }
1700b2fc195eSAndrew Gallatin }
1701b2fc195eSAndrew Gallatin 
1702b2fc195eSAndrew Gallatin /*
1703b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1704b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1705b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1706b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1707b2fc195eSAndrew Gallatin  */
1708b2fc195eSAndrew Gallatin 
1709b2fc195eSAndrew Gallatin static inline void
17101e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1711b2fc195eSAndrew Gallatin                   int cnt)
1712b2fc195eSAndrew Gallatin {
1713b2fc195eSAndrew Gallatin         int idx, i;
1714b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1715b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1716b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1717b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17185e7d8541SAndrew Gallatin 	uint8_t last_flags;
1719b2fc195eSAndrew Gallatin 
1720b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1721b2fc195eSAndrew Gallatin 
17225e7d8541SAndrew Gallatin 	last_flags = src->flags;
17235e7d8541SAndrew Gallatin 	src->flags = 0;
172473c7c83fSAndrew Gallatin         wmb();
1725b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1726b2fc195eSAndrew Gallatin         srcp = src;
1727b2fc195eSAndrew Gallatin 
1728b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1729b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17306d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
173173c7c83fSAndrew Gallatin                         wmb(); /* force write every 32 bytes */
1732b2fc195eSAndrew Gallatin                         srcp += 2;
1733b2fc195eSAndrew Gallatin                         dstp += 2;
1734b2fc195eSAndrew Gallatin                 }
1735b2fc195eSAndrew Gallatin         } else {
1736b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1737b2fc195eSAndrew Gallatin                    that it is submitted below */
17386d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1739b2fc195eSAndrew Gallatin                 i = 0;
1740b2fc195eSAndrew Gallatin         }
1741b2fc195eSAndrew Gallatin         if (i < cnt) {
1742b2fc195eSAndrew Gallatin                 /* submit the first request */
17436d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
174473c7c83fSAndrew Gallatin                 wmb(); /* barrier before setting valid flag */
1745b2fc195eSAndrew Gallatin         }
1746b2fc195eSAndrew Gallatin 
1747b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17485e7d8541SAndrew Gallatin         src->flags = last_flags;
1749b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1750b2fc195eSAndrew Gallatin         src_ints+=3;
1751b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1752b2fc195eSAndrew Gallatin         dst_ints+=3;
1753b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1754b2fc195eSAndrew Gallatin         tx->req += cnt;
175573c7c83fSAndrew Gallatin         wmb();
1756b2fc195eSAndrew Gallatin }
1757b2fc195eSAndrew Gallatin 
175837d89b0cSAndrew Gallatin #if IFCAP_TSO4
175937d89b0cSAndrew Gallatin 
1760b2fc195eSAndrew Gallatin static void
17611e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17621e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1763aed8e389SAndrew Gallatin {
17641e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1765aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1766aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1767aed8e389SAndrew Gallatin 	struct ip *ip;
1768aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1769aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1770aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1771aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1772aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1773aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1774aed8e389SAndrew Gallatin 	static int once;
1775aed8e389SAndrew Gallatin 
1776aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1777aed8e389SAndrew Gallatin 
1778aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1779aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1780aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1781aed8e389SAndrew Gallatin 	 */
1782aed8e389SAndrew Gallatin 
1783aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1784aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1785aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1786c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1787c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
17881e413cf9SAndrew Gallatin 			   ss->scratch);
17891e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1790aed8e389SAndrew Gallatin 	} else {
1791c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1792aed8e389SAndrew Gallatin 	}
1793c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1794aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1795c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
17961e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1797c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1798aed8e389SAndrew Gallatin 	}
1799aed8e389SAndrew Gallatin 
1800aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1801c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1802aed8e389SAndrew Gallatin 
1803aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1804c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1805aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1806aed8e389SAndrew Gallatin 
1807aed8e389SAndrew Gallatin 
1808aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1809aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1810aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1811aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1812aed8e389SAndrew Gallatin 
18131e413cf9SAndrew Gallatin 	tx = &ss->tx;
1814aed8e389SAndrew Gallatin 	req = tx->req_list;
1815aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1816aed8e389SAndrew Gallatin 	cnt = 0;
1817aed8e389SAndrew Gallatin 	rdma_count = 0;
1818aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1819aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1820aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1821aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1822aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1823aed8e389SAndrew Gallatin 	 *
1824aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1825aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1826aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1827aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1828aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1829aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1830aed8e389SAndrew Gallatin 	 *
1831aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1832aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1833aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1834aed8e389SAndrew Gallatin 	 */
1835aed8e389SAndrew Gallatin 
1836aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1837aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1838aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1839aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1840e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1841aed8e389SAndrew Gallatin 
1842aed8e389SAndrew Gallatin 		while (len) {
1843aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1844e39a0a37SAndrew Gallatin 			seglen = len;
1845aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1846aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1847aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1848aed8e389SAndrew Gallatin 				/* payload */
1849aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1850aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1851aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1852aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1853aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1854aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1855aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1856aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1857aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1858aed8e389SAndrew Gallatin 				/* header ends */
1859aed8e389SAndrew Gallatin 				rdma_count = -1;
1860aed8e389SAndrew Gallatin 				cum_len_next = 0;
1861aed8e389SAndrew Gallatin 				seglen = -cum_len;
1862aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1863aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1864aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1865aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1866aed8e389SAndrew Gallatin 			    }
1867aed8e389SAndrew Gallatin 
1868aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1869aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1870aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1871aed8e389SAndrew Gallatin 			req->pad = 0;
1872aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1873aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1874aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1875aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1876aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1877aed8e389SAndrew Gallatin 			low += seglen;
1878aed8e389SAndrew Gallatin 			len -= seglen;
1879aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1880aed8e389SAndrew Gallatin 			flags = flags_next;
1881aed8e389SAndrew Gallatin 			req++;
1882aed8e389SAndrew Gallatin 			cnt++;
1883aed8e389SAndrew Gallatin 			rdma_count++;
1884aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1885aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1886aed8e389SAndrew Gallatin 			else
1887aed8e389SAndrew Gallatin 				cksum_offset = 0;
1888adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1889aed8e389SAndrew Gallatin 				goto drop;
1890aed8e389SAndrew Gallatin 		}
1891aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1892aed8e389SAndrew Gallatin 		seg++;
1893aed8e389SAndrew Gallatin 	}
1894aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1895aed8e389SAndrew Gallatin 
1896aed8e389SAndrew Gallatin 	do {
1897aed8e389SAndrew Gallatin 		req--;
1898aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1899aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1900aed8e389SAndrew Gallatin 
1901aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1902aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1903c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
1904c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1905c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1906c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1907c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1908c6cb3e3fSAndrew Gallatin 		tx->activate++;
1909c6cb3e3fSAndrew Gallatin 		wmb();
1910c6cb3e3fSAndrew Gallatin 	}
1911c6cb3e3fSAndrew Gallatin #endif
1912aed8e389SAndrew Gallatin 	return;
1913aed8e389SAndrew Gallatin 
1914aed8e389SAndrew Gallatin drop:
1915e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1916aed8e389SAndrew Gallatin 	m_freem(m);
1917c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1918aed8e389SAndrew Gallatin 	if (!once) {
1919adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1920adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1921adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1922aed8e389SAndrew Gallatin 		once = 1;
1923aed8e389SAndrew Gallatin 	}
1924aed8e389SAndrew Gallatin 	return;
1925aed8e389SAndrew Gallatin 
1926aed8e389SAndrew Gallatin }
1927aed8e389SAndrew Gallatin 
192837d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
192937d89b0cSAndrew Gallatin 
193037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1931c792928fSAndrew Gallatin /*
1932c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1933c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1934c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1935c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1936c792928fSAndrew Gallatin  */
1937c792928fSAndrew Gallatin static struct mbuf *
1938c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1939c792928fSAndrew Gallatin {
1940c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1941c792928fSAndrew Gallatin 
1942c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1943c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1944c792928fSAndrew Gallatin 		return NULL;
1945c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1946c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1947c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1948c792928fSAndrew Gallatin 			return NULL;
1949c792928fSAndrew Gallatin 	}
1950c792928fSAndrew Gallatin 	/*
1951c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1952c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1953c792928fSAndrew Gallatin 	 */
1954c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1955c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1956c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1957c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1958c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1959c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1960c792928fSAndrew Gallatin 	return m;
1961c792928fSAndrew Gallatin }
196237d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
1963c792928fSAndrew Gallatin 
1964aed8e389SAndrew Gallatin static void
19651e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
1966b2fc195eSAndrew Gallatin {
19671e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
1968b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1969b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1970b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1971b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19721e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1973b2fc195eSAndrew Gallatin 	struct ip *ip;
1974c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1975aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1976aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1977b2fc195eSAndrew Gallatin 
1978b2fc195eSAndrew Gallatin 
19791e413cf9SAndrew Gallatin 	sc = ss->sc;
1980b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19811e413cf9SAndrew Gallatin 	tx = &ss->tx;
1982b2fc195eSAndrew Gallatin 
1983c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
198437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1985c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1986c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1987c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1988c792928fSAndrew Gallatin 			goto drop;
1989c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1990c792928fSAndrew Gallatin 	}
199137d89b0cSAndrew Gallatin #endif
1992b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1993b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1994b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1995aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1996b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1997adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1998b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1999b2fc195eSAndrew Gallatin 		   to defrag */
2000b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2001b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2002b2fc195eSAndrew Gallatin 			goto drop;
2003b2fc195eSAndrew Gallatin 		}
20041e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2005b2fc195eSAndrew Gallatin 		m = m_tmp;
2006b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2007b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2008aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2009b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2010b2fc195eSAndrew Gallatin 	}
2011adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2012aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2013aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2014b2fc195eSAndrew Gallatin 		goto drop;
2015b2fc195eSAndrew Gallatin 	}
2016b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2017b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20185e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2019b2fc195eSAndrew Gallatin 
202037d89b0cSAndrew Gallatin #if IFCAP_TSO4
2021aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2022aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20231e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
2024aed8e389SAndrew Gallatin 		return;
2025aed8e389SAndrew Gallatin 	}
202637d89b0cSAndrew Gallatin #endif
2027aed8e389SAndrew Gallatin 
2028b2fc195eSAndrew Gallatin 	req = tx->req_list;
2029b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20305e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20315e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2032b2fc195eSAndrew Gallatin 
2033b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2034b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2035aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2036aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2037c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2038c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20391e413cf9SAndrew Gallatin 				   ss->scratch);
20401e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2041aed8e389SAndrew Gallatin 		} else {
2042c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2043aed8e389SAndrew Gallatin 		}
2044c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2045b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20465e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2047b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20485e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2049aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2050aed8e389SAndrew Gallatin 	} else {
2051aed8e389SAndrew Gallatin 		odd_flag = 0;
2052b2fc195eSAndrew Gallatin 	}
20535e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20545e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2055b2fc195eSAndrew Gallatin 
2056b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2057b2fc195eSAndrew Gallatin 	cum_len = 0;
2058aed8e389SAndrew Gallatin 	seg = tx->seg_list;
20595e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2060b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2061b2fc195eSAndrew Gallatin 		req->addr_low =
20626d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2063b2fc195eSAndrew Gallatin 		req->addr_high =
20646d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2065b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2066b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2067b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2068b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2069b2fc195eSAndrew Gallatin 		else
2070b2fc195eSAndrew Gallatin 			cksum_offset = 0;
20715e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20725e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20735e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2074aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2075b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2076b2fc195eSAndrew Gallatin 		seg++;
2077b2fc195eSAndrew Gallatin 		req++;
2078b2fc195eSAndrew Gallatin 		req->flags = 0;
2079b2fc195eSAndrew Gallatin 	}
2080b2fc195eSAndrew Gallatin 	req--;
2081b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2082b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2083b2fc195eSAndrew Gallatin 		req++;
2084b2fc195eSAndrew Gallatin 		req->addr_low =
20856d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2086b2fc195eSAndrew Gallatin 		req->addr_high =
20876d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2088b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
20895e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
20905e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20915e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20925e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2093aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2094b2fc195eSAndrew Gallatin 		cnt++;
2095b2fc195eSAndrew Gallatin 	}
20965e7d8541SAndrew Gallatin 
20975e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
20985e7d8541SAndrew Gallatin #if 0
20995e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21005e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21015e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21025e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21035e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21045e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21055e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21065e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21075e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21085e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21095e7d8541SAndrew Gallatin 	}
21105e7d8541SAndrew Gallatin 	printf("--------------\n");
21115e7d8541SAndrew Gallatin #endif
21125e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21136d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2114c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2115c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2116c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2117c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2118c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2119c6cb3e3fSAndrew Gallatin 		tx->activate++;
2120c6cb3e3fSAndrew Gallatin 		wmb();
2121c6cb3e3fSAndrew Gallatin 	}
2122c6cb3e3fSAndrew Gallatin #endif
2123b2fc195eSAndrew Gallatin 	return;
2124b2fc195eSAndrew Gallatin 
2125b2fc195eSAndrew Gallatin drop:
2126b2fc195eSAndrew Gallatin 	m_freem(m);
2127c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2128b2fc195eSAndrew Gallatin 	return;
2129b2fc195eSAndrew Gallatin }
2130b2fc195eSAndrew Gallatin 
2131c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2132c6cb3e3fSAndrew Gallatin static void
2133c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp)
2134c6cb3e3fSAndrew Gallatin {
2135c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2136c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2137c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2138c6cb3e3fSAndrew Gallatin 	int slice;
2139b2fc195eSAndrew Gallatin 
2140c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2141c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2142c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2143c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2144c6cb3e3fSAndrew Gallatin 			m_freem(m);
2145c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2146c6cb3e3fSAndrew Gallatin 	}
2147c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2148c6cb3e3fSAndrew Gallatin }
21496d914a32SAndrew Gallatin 
2150c6cb3e3fSAndrew Gallatin static inline void
2151c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2152c6cb3e3fSAndrew Gallatin {
2153c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2154c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2155c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2156c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2157c6cb3e3fSAndrew Gallatin 
2158c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2159c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2160c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2161c6cb3e3fSAndrew Gallatin 
2162c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2163c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2164c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2165c6cb3e3fSAndrew Gallatin 			return;
2166c6cb3e3fSAndrew Gallatin 		}
2167c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2168c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2169c6cb3e3fSAndrew Gallatin 
2170c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2171c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2172c6cb3e3fSAndrew Gallatin 	}
2173c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2174c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2175c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2176c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2177c6cb3e3fSAndrew Gallatin 		tx->stall++;
2178c6cb3e3fSAndrew Gallatin 	}
2179c6cb3e3fSAndrew Gallatin }
2180c6cb3e3fSAndrew Gallatin 
2181c6cb3e3fSAndrew Gallatin static int
2182c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2183c6cb3e3fSAndrew Gallatin {
2184c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2185c6cb3e3fSAndrew Gallatin 	struct ifnet *ifp;
2186c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2187c6cb3e3fSAndrew Gallatin 	int err;
2188c6cb3e3fSAndrew Gallatin 
2189c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2190c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2191c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2192c6cb3e3fSAndrew Gallatin 
2193c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2194c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2195c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2196c6cb3e3fSAndrew Gallatin 		return (err);
2197c6cb3e3fSAndrew Gallatin 	}
2198c6cb3e3fSAndrew Gallatin 
2199c6cb3e3fSAndrew Gallatin 	if (drbr_empty(ifp, tx->br) &&
2200c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2201c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2202c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2203c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2204c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2205c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2206c6cb3e3fSAndrew Gallatin 		return (err);
2207c6cb3e3fSAndrew Gallatin 	}
2208c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2209c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2210c6cb3e3fSAndrew Gallatin 	return (0);
2211c6cb3e3fSAndrew Gallatin }
2212c6cb3e3fSAndrew Gallatin 
2213c6cb3e3fSAndrew Gallatin static int
2214c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m)
2215c6cb3e3fSAndrew Gallatin {
2216c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2217c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2218c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2219c6cb3e3fSAndrew Gallatin 	int err = 0;
2220c6cb3e3fSAndrew Gallatin 	int slice;
2221c6cb3e3fSAndrew Gallatin 
2222c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2223c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2224c6cb3e3fSAndrew Gallatin 
2225c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2226c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2227c6cb3e3fSAndrew Gallatin 
2228c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2229c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2230c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2231c6cb3e3fSAndrew Gallatin 	} else {
2232c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2233c6cb3e3fSAndrew Gallatin 	}
2234c6cb3e3fSAndrew Gallatin 
2235c6cb3e3fSAndrew Gallatin 	return (err);
2236c6cb3e3fSAndrew Gallatin }
2237c6cb3e3fSAndrew Gallatin 
2238c6cb3e3fSAndrew Gallatin #else
22396d914a32SAndrew Gallatin 
22406d914a32SAndrew Gallatin static inline void
22411e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2242b2fc195eSAndrew Gallatin {
22431e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2244b2fc195eSAndrew Gallatin 	struct mbuf *m;
2245b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
22461e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2247b2fc195eSAndrew Gallatin 
22481e413cf9SAndrew Gallatin 	sc = ss->sc;
2249b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
22501e413cf9SAndrew Gallatin 	tx = &ss->tx;
2251adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
22526d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
22536d914a32SAndrew Gallatin 		if (m == NULL) {
22546d914a32SAndrew Gallatin 			return;
22556d914a32SAndrew Gallatin 		}
2256b2fc195eSAndrew Gallatin 		/* let BPF see it */
2257b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2258b2fc195eSAndrew Gallatin 
2259b2fc195eSAndrew Gallatin 		/* give it to the nic */
22601e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
22616d914a32SAndrew Gallatin 	}
22626d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2263a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2264b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2265adae7080SAndrew Gallatin 		tx->stall++;
2266a82c2581SAndrew Gallatin 	}
2267b2fc195eSAndrew Gallatin }
2268c6cb3e3fSAndrew Gallatin #endif
2269b2fc195eSAndrew Gallatin static void
22706d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2271b2fc195eSAndrew Gallatin {
22726d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
22731e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2274b2fc195eSAndrew Gallatin 
22751e413cf9SAndrew Gallatin 	/* only use the first slice for now */
22761e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
22771e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
22781e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
22791e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2280b2fc195eSAndrew Gallatin }
2281b2fc195eSAndrew Gallatin 
22825e7d8541SAndrew Gallatin /*
22835e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
22845e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
22855e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
22865e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
22875e7d8541SAndrew Gallatin  * in a burst
22885e7d8541SAndrew Gallatin  */
22895e7d8541SAndrew Gallatin static inline void
22905e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
22915e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
22925e7d8541SAndrew Gallatin {
22935e7d8541SAndrew Gallatin 	uint32_t low;
22945e7d8541SAndrew Gallatin 
22955e7d8541SAndrew Gallatin 	low = src->addr_low;
22965e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2297a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
229873c7c83fSAndrew Gallatin 	wmb();
2299a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
230073c7c83fSAndrew Gallatin 	wmb();
230140385a5fSAndrew Gallatin 	src->addr_low = low;
23025e7d8541SAndrew Gallatin 	dst->addr_low = low;
230373c7c83fSAndrew Gallatin 	wmb();
23045e7d8541SAndrew Gallatin }
23055e7d8541SAndrew Gallatin 
2306b2fc195eSAndrew Gallatin static int
23071e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2308b2fc195eSAndrew Gallatin {
2309b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2310b2fc195eSAndrew Gallatin 	struct mbuf *m;
23111e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2312b2fc195eSAndrew Gallatin 	int cnt, err;
2313b2fc195eSAndrew Gallatin 
2314b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2315b2fc195eSAndrew Gallatin 	if (m == NULL) {
2316b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2317b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2318b2fc195eSAndrew Gallatin 		goto done;
2319b2fc195eSAndrew Gallatin 	}
2320b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2321b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2322b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2323b2fc195eSAndrew Gallatin 	if (err != 0) {
2324b2fc195eSAndrew Gallatin 		m_free(m);
2325b2fc195eSAndrew Gallatin 		goto done;
2326b2fc195eSAndrew Gallatin 	}
2327b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2328b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23296d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2330b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23316d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2332b2fc195eSAndrew Gallatin 
2333b2fc195eSAndrew Gallatin done:
2334adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2335adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2336b2fc195eSAndrew Gallatin 	return err;
2337b2fc195eSAndrew Gallatin }
2338b2fc195eSAndrew Gallatin 
2339b2fc195eSAndrew Gallatin static int
23401e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2341b2fc195eSAndrew Gallatin {
2342053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2343b2fc195eSAndrew Gallatin 	struct mbuf *m;
23441e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2345053e637fSAndrew Gallatin 	int cnt, err, i;
2346b2fc195eSAndrew Gallatin 
2347a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2348a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2349a0394e33SAndrew Gallatin 	else
2350053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2351b2fc195eSAndrew Gallatin 	if (m == NULL) {
2352b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2353b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2354b2fc195eSAndrew Gallatin 		goto done;
2355b2fc195eSAndrew Gallatin 	}
23564d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2357b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2358053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2359b2fc195eSAndrew Gallatin 	if (err != 0) {
2360b2fc195eSAndrew Gallatin 		m_free(m);
2361b2fc195eSAndrew Gallatin 		goto done;
2362b2fc195eSAndrew Gallatin 	}
2363b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2364b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2365b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2366b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2367b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2368053e637fSAndrew Gallatin 
2369b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2370b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2371053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2372053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2373053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2374053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2375053e637fSAndrew Gallatin        }
2376b0f7b922SAndrew Gallatin #endif
2377b2fc195eSAndrew Gallatin 
2378b2fc195eSAndrew Gallatin done:
2379053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2380b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
23815e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
23825e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2383b2fc195eSAndrew Gallatin 		}
2384053e637fSAndrew Gallatin 		idx++;
2385053e637fSAndrew Gallatin 	}
2386b2fc195eSAndrew Gallatin 	return err;
2387b2fc195eSAndrew Gallatin }
2388b2fc195eSAndrew Gallatin 
23899b03b0f3SAndrew Gallatin /*
23909b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
23919b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
23929b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
23939b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2394053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2395053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
23969b03b0f3SAndrew Gallatin  */
23979b03b0f3SAndrew Gallatin 
2398053e637fSAndrew Gallatin static inline uint16_t
2399053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2400053e637fSAndrew Gallatin {
2401053e637fSAndrew Gallatin 	struct ether_header *eh;
2402053e637fSAndrew Gallatin 	struct ip *ip;
2403053e637fSAndrew Gallatin 	uint16_t c;
2404053e637fSAndrew Gallatin 
2405053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2406053e637fSAndrew Gallatin 
2407053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2408053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2409053e637fSAndrew Gallatin 		return 1;
2410053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2411053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2412053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2413053e637fSAndrew Gallatin 		return 1;
2414eb6219e3SAndrew Gallatin #ifdef INET
2415053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2416053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2417053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2418eb6219e3SAndrew Gallatin #else
2419eb6219e3SAndrew Gallatin 	c = 1;
2420eb6219e3SAndrew Gallatin #endif
2421053e637fSAndrew Gallatin 	c ^= 0xffff;
2422053e637fSAndrew Gallatin 	return (c);
24235e7d8541SAndrew Gallatin }
2424053e637fSAndrew Gallatin 
2425c792928fSAndrew Gallatin static void
2426c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2427c792928fSAndrew Gallatin {
2428c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2429c792928fSAndrew Gallatin 	struct ether_header *eh;
2430c792928fSAndrew Gallatin 	uint32_t partial;
2431c792928fSAndrew Gallatin 
2432c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2433c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2434c792928fSAndrew Gallatin 
2435c792928fSAndrew Gallatin 	/*
2436c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2437c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2438c792928fSAndrew Gallatin 	 * header.
2439c792928fSAndrew Gallatin 	 */
2440c792928fSAndrew Gallatin 
2441c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2442c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2443c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2444c792928fSAndrew Gallatin 	(*csum) += ~partial;
2445c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2446c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2447c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2448c792928fSAndrew Gallatin 
2449c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2450c792928fSAndrew Gallatin 	   later consumers expect this */
2451c792928fSAndrew Gallatin 	*csum = htons(*csum);
2452c792928fSAndrew Gallatin 
2453c792928fSAndrew Gallatin 	/* save the tag */
245437d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2455c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
245637d89b0cSAndrew Gallatin #else
245737d89b0cSAndrew Gallatin 	{
245837d89b0cSAndrew Gallatin 		struct m_tag *mtag;
245937d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
246037d89b0cSAndrew Gallatin 				   M_NOWAIT);
246137d89b0cSAndrew Gallatin 		if (mtag == NULL)
246237d89b0cSAndrew Gallatin 			return;
246337d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
246437d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
246537d89b0cSAndrew Gallatin 	}
246637d89b0cSAndrew Gallatin 
246737d89b0cSAndrew Gallatin #endif
246837d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2469c792928fSAndrew Gallatin 
2470c792928fSAndrew Gallatin 	/*
2471c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2472c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2473c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2474c792928fSAndrew Gallatin 	 * type field is already in place.
2475c792928fSAndrew Gallatin 	 */
2476c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2477c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2478c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2479c792928fSAndrew Gallatin }
2480c792928fSAndrew Gallatin 
24815e7d8541SAndrew Gallatin 
24825e7d8541SAndrew Gallatin static inline void
24831e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2484b2fc195eSAndrew Gallatin {
24851e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2486b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2487053e637fSAndrew Gallatin 	struct mbuf *m;
2488c792928fSAndrew Gallatin 	struct ether_header *eh;
24891e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2490053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2491b2fc195eSAndrew Gallatin 	int idx;
2492053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2493b2fc195eSAndrew Gallatin 
24941e413cf9SAndrew Gallatin 	sc = ss->sc;
2495b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
24961e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2497b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2498053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2499b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2500b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2501b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25021e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2503053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2504053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2505053e637fSAndrew Gallatin 		return;
2506b2fc195eSAndrew Gallatin 	}
2507053e637fSAndrew Gallatin 
2508b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2509b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2510b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2511b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2512b2fc195eSAndrew Gallatin 
2513b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2514b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2515b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2516b2fc195eSAndrew Gallatin 
2517053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2518053e637fSAndrew Gallatin 	 * aligned */
25195e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2520b2fc195eSAndrew Gallatin 
2521053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2522053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25231e413cf9SAndrew Gallatin 	ss->ipackets++;
2524c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2525c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2526c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2527c792928fSAndrew Gallatin 	}
2528b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2529053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25301e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2531b2fc195eSAndrew Gallatin 			return;
2532053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2533053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2534053e637fSAndrew Gallatin 		   checksum is good */
2535053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2536053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2537b2fc195eSAndrew Gallatin 	}
2538c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2539c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2540c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2541c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2542c6cb3e3fSAndrew Gallatin 	}
2543053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2544053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2545b2fc195eSAndrew Gallatin }
2546b2fc195eSAndrew Gallatin 
2547b2fc195eSAndrew Gallatin static inline void
25481e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2549b2fc195eSAndrew Gallatin {
25501e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2551b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2552c792928fSAndrew Gallatin 	struct ether_header *eh;
2553b2fc195eSAndrew Gallatin 	struct mbuf *m;
25541e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2555b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2556b2fc195eSAndrew Gallatin 	int idx;
2557053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2558b2fc195eSAndrew Gallatin 
25591e413cf9SAndrew Gallatin 	sc = ss->sc;
2560b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25611e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2562b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2563b2fc195eSAndrew Gallatin 	rx->cnt++;
2564b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2565b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2566b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25671e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2568b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2569b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2570b2fc195eSAndrew Gallatin 		return;
2571b2fc195eSAndrew Gallatin 	}
2572b2fc195eSAndrew Gallatin 
2573b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2574b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2575b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2576b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2577b2fc195eSAndrew Gallatin 
2578b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2579b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2580b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2581b2fc195eSAndrew Gallatin 
2582b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2583b2fc195eSAndrew Gallatin 	 * aligned */
25845e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2585b2fc195eSAndrew Gallatin 
25869b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
25879b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25881e413cf9SAndrew Gallatin 	ss->ipackets++;
2589c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2590c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2591c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2592c792928fSAndrew Gallatin 	}
2593b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2594053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25951e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2596053e637fSAndrew Gallatin 			return;
2597053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2598053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2599053e637fSAndrew Gallatin 		   checksum is good */
2600053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2601053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2602053e637fSAndrew Gallatin 	}
2603c6cb3e3fSAndrew Gallatin 	/* flowid only valid if RSS hashing is enabled */
2604c6cb3e3fSAndrew Gallatin 	if (sc->num_slices > 1) {
2605c6cb3e3fSAndrew Gallatin 		m->m_pkthdr.flowid = (ss - sc->ss);
2606c6cb3e3fSAndrew Gallatin 		m->m_flags |= M_FLOWID;
2607c6cb3e3fSAndrew Gallatin 	}
2608b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2609b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2610b2fc195eSAndrew Gallatin }
2611b2fc195eSAndrew Gallatin 
2612b2fc195eSAndrew Gallatin static inline void
26131e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26145e7d8541SAndrew Gallatin {
26151e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26165e7d8541SAndrew Gallatin 	int limit = 0;
26175e7d8541SAndrew Gallatin 	uint16_t length;
26185e7d8541SAndrew Gallatin 	uint16_t checksum;
26195e7d8541SAndrew Gallatin 
26205e7d8541SAndrew Gallatin 
26215e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26225e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
26235e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2624053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2625b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
26261e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
26275e7d8541SAndrew Gallatin 		else
26281e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
26295e7d8541SAndrew Gallatin 		rx_done->cnt++;
2630adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
26315e7d8541SAndrew Gallatin 
26325e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2633f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
26345e7d8541SAndrew Gallatin 			break;
2635053e637fSAndrew Gallatin 	}
2636eb6219e3SAndrew Gallatin #ifdef INET
26371e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
2638eb6219e3SAndrew Gallatin 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
26391e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
26401e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
26415e7d8541SAndrew Gallatin 	}
2642eb6219e3SAndrew Gallatin #endif
26435e7d8541SAndrew Gallatin }
26445e7d8541SAndrew Gallatin 
26455e7d8541SAndrew Gallatin 
26465e7d8541SAndrew Gallatin static inline void
26471e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2648b2fc195eSAndrew Gallatin {
2649b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
26501e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2651b2fc195eSAndrew Gallatin 	struct mbuf *m;
2652b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2653f616ebc7SAndrew Gallatin 	int idx;
2654c6cb3e3fSAndrew Gallatin 	int *flags;
2655b2fc195eSAndrew Gallatin 
26561e413cf9SAndrew Gallatin 	tx = &ss->tx;
26571e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
26585e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2659b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2660b2fc195eSAndrew Gallatin 		tx->done++;
2661b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2662b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2663b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2664b2fc195eSAndrew Gallatin 		if (m != NULL) {
266571032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
266671032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
266771032832SAndrew Gallatin 				ss->omcasts++;
2668c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2669b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2670b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2671b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2672b2fc195eSAndrew Gallatin 			m_freem(m);
2673b2fc195eSAndrew Gallatin 		}
26745e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
26755e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
26765e7d8541SAndrew Gallatin 			tx->pkt_done++;
26775e7d8541SAndrew Gallatin 		}
2678b2fc195eSAndrew Gallatin 	}
2679b2fc195eSAndrew Gallatin 
2680b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2681b2fc195eSAndrew Gallatin            its OK to send packets */
2682c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2683c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
2684c6cb3e3fSAndrew Gallatin #else
2685c6cb3e3fSAndrew Gallatin 	flags = &ifp->if_drv_flags;
2686c6cb3e3fSAndrew Gallatin #endif
26871e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2688c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2689c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2690c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
26911e413cf9SAndrew Gallatin 		ss->tx.wake++;
26921e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2693b2fc195eSAndrew Gallatin 	}
2694c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
2695c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2696c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2697c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2698c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2699c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2700c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2701c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2702c6cb3e3fSAndrew Gallatin 			wmb();
2703c6cb3e3fSAndrew Gallatin 		}
2704c6cb3e3fSAndrew Gallatin 	}
2705c6cb3e3fSAndrew Gallatin #endif
2706c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2707c6cb3e3fSAndrew Gallatin 
2708b2fc195eSAndrew Gallatin }
2709b2fc195eSAndrew Gallatin 
271001638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2711c587e59fSAndrew Gallatin {
2712c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2713c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2714c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2715c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
271601638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2717c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2718c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2719c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2720c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2721c587e59fSAndrew Gallatin };
272201638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
272301638550SAndrew Gallatin {
27244ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
272501638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
272601638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
272701638550SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
272801638550SAndrew Gallatin };
2729c587e59fSAndrew Gallatin 
2730c587e59fSAndrew Gallatin static void
2731c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2732c587e59fSAndrew Gallatin {
2733c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2734c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2735c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2736c587e59fSAndrew Gallatin }
2737c587e59fSAndrew Gallatin 
2738c587e59fSAndrew Gallatin 
2739c587e59fSAndrew Gallatin /*
2740c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2741c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2742c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2743c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2744c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2745c587e59fSAndrew Gallatin  * once, not each time the link is up.
2746c587e59fSAndrew Gallatin  */
2747c587e59fSAndrew Gallatin static void
2748c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2749c587e59fSAndrew Gallatin {
2750c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
275101638550SAndrew Gallatin 	char *cage_type;
2752c587e59fSAndrew Gallatin 	char *ptr;
275301638550SAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
275401638550SAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
275501638550SAndrew Gallatin 	uint32_t byte;
2756c587e59fSAndrew Gallatin 
2757c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2758c587e59fSAndrew Gallatin 
2759c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2760c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2761c587e59fSAndrew Gallatin 		return;
2762c587e59fSAndrew Gallatin 
2763c587e59fSAndrew Gallatin 	/*
2764c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2765c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2766c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2767c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2768c587e59fSAndrew Gallatin 	 */
2769c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2770c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2771c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2772c587e59fSAndrew Gallatin 	}
2773c587e59fSAndrew Gallatin 
2774c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
277537d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2776c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2777c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2778c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2779c587e59fSAndrew Gallatin 			return;
2780c587e59fSAndrew Gallatin 		}
2781c587e59fSAndrew Gallatin 	}
2782c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
278301638550SAndrew Gallatin 		/* -C is CX4 */
2784c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2785c587e59fSAndrew Gallatin 		return;
2786c587e59fSAndrew Gallatin 	}
2787c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
278801638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2789c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2790c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2791c587e59fSAndrew Gallatin 		return;
2792c587e59fSAndrew Gallatin 	}
2793c587e59fSAndrew Gallatin 
279401638550SAndrew Gallatin 	if (*ptr == 'R') {
279501638550SAndrew Gallatin 		/* -R is XFP */
279601638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
279701638550SAndrew Gallatin 		mxge_media_type_entries =
279801638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types) /
279901638550SAndrew Gallatin 			sizeof (mxge_xfp_media_types[0]);
280001638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
280101638550SAndrew Gallatin 		cage_type = "XFP";
280201638550SAndrew Gallatin 	}
280301638550SAndrew Gallatin 
280401638550SAndrew Gallatin 	if (*ptr == 'S' || *(ptr +1) == 'S') {
280501638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
280601638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
280701638550SAndrew Gallatin 		mxge_media_type_entries =
280801638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types) /
280901638550SAndrew Gallatin 			sizeof (mxge_sfp_media_types[0]);
281001638550SAndrew Gallatin 		cage_type = "SFP+";
281101638550SAndrew Gallatin 		byte = 3;
281201638550SAndrew Gallatin 	}
281301638550SAndrew Gallatin 
281401638550SAndrew Gallatin 	if (mxge_media_types == NULL) {
2815c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2816c587e59fSAndrew Gallatin 		return;
2817c587e59fSAndrew Gallatin 	}
2818c587e59fSAndrew Gallatin 
2819c587e59fSAndrew Gallatin 	/*
2820c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2821c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2822c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2823c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2824c587e59fSAndrew Gallatin 	 * a millisecond
2825c587e59fSAndrew Gallatin 	 */
2826c587e59fSAndrew Gallatin 
2827c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
282801638550SAndrew Gallatin 	cmd.data1 = byte;
282901638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
283001638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2831c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2832c587e59fSAndrew Gallatin 	}
283301638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
283401638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2835c587e59fSAndrew Gallatin 	}
2836c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2837c587e59fSAndrew Gallatin 		return;
2838c587e59fSAndrew Gallatin 	}
2839c587e59fSAndrew Gallatin 
2840c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
284101638550SAndrew Gallatin 	cmd.data0 = byte;
284201638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2843c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2844c587e59fSAndrew Gallatin 		DELAY(1000);
284501638550SAndrew Gallatin 		cmd.data0 = byte;
284601638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2847c587e59fSAndrew Gallatin 	}
2848c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
284901638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
285001638550SAndrew Gallatin 			      cage_type, err, ms);
2851c587e59fSAndrew Gallatin 		return;
2852c587e59fSAndrew Gallatin 	}
2853c587e59fSAndrew Gallatin 
2854c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2855c587e59fSAndrew Gallatin 		if (mxge_verbose)
285601638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2857c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2858c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2859c587e59fSAndrew Gallatin 		return;
2860c587e59fSAndrew Gallatin 	}
286101638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2862c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2863c587e59fSAndrew Gallatin 			if (mxge_verbose)
286401638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
286501638550SAndrew Gallatin 					      cage_type,
2866c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2867c587e59fSAndrew Gallatin 
2868c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2869c587e59fSAndrew Gallatin 			return;
2870c587e59fSAndrew Gallatin 		}
2871c587e59fSAndrew Gallatin 	}
287201638550SAndrew Gallatin 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
287301638550SAndrew Gallatin 		      cmd.data0);
2874c587e59fSAndrew Gallatin 
2875c587e59fSAndrew Gallatin 	return;
2876c587e59fSAndrew Gallatin }
2877c587e59fSAndrew Gallatin 
2878b2fc195eSAndrew Gallatin static void
28796d87a65dSAndrew Gallatin mxge_intr(void *arg)
2880b2fc195eSAndrew Gallatin {
28811e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
28821e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
28831e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
28841e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
28851e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
28865e7d8541SAndrew Gallatin 	uint32_t send_done_count;
28875e7d8541SAndrew Gallatin 	uint8_t valid;
2888b2fc195eSAndrew Gallatin 
2889b2fc195eSAndrew Gallatin 
2890c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
28911e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
28921e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
28931e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
28941e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
28951e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
28961e413cf9SAndrew Gallatin 		return;
28971e413cf9SAndrew Gallatin 	}
2898c6cb3e3fSAndrew Gallatin #endif
28991e413cf9SAndrew Gallatin 
29005e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29015e7d8541SAndrew Gallatin 	if (!stats->valid) {
29025e7d8541SAndrew Gallatin 		return;
2903b2fc195eSAndrew Gallatin 	}
29045e7d8541SAndrew Gallatin 	valid = stats->valid;
2905b2fc195eSAndrew Gallatin 
290691ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29075e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29085e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29095e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29105e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29115e7d8541SAndrew Gallatin 			stats->valid = 0;
2912dc8731d4SAndrew Gallatin 	} else {
2913dc8731d4SAndrew Gallatin 		stats->valid = 0;
2914dc8731d4SAndrew Gallatin 	}
2915dc8731d4SAndrew Gallatin 
2916dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29175e7d8541SAndrew Gallatin 	do {
29185e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29195e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29205e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29215e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2922c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29231e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29241e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
29255e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2926b2fc195eSAndrew Gallatin 		}
292791ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
292873c7c83fSAndrew Gallatin 			wmb();
29295e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2930b2fc195eSAndrew Gallatin 
2931c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
2932c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
29335e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
29345e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2935b2fc195eSAndrew Gallatin 			if (sc->link_state) {
29365e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
29375e7d8541SAndrew Gallatin 				if (mxge_verbose)
29385e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2939b2fc195eSAndrew Gallatin 			} else {
29405e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
29415e7d8541SAndrew Gallatin 				if (mxge_verbose)
29425e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2943b2fc195eSAndrew Gallatin 			}
2944c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2945b2fc195eSAndrew Gallatin 		}
2946b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
29471e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2948b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
29491e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
29505e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
29515e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
29525e7d8541SAndrew Gallatin 		}
2953c587e59fSAndrew Gallatin 
2954c587e59fSAndrew Gallatin 		if (stats->link_down) {
29555e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
2956c587e59fSAndrew Gallatin 			sc->link_state = 0;
2957c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2958c587e59fSAndrew Gallatin 		}
2959b2fc195eSAndrew Gallatin 	}
2960b2fc195eSAndrew Gallatin 
29615e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
29625e7d8541SAndrew Gallatin 	if (valid & 0x1)
29631e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
29641e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
2965b2fc195eSAndrew Gallatin }
2966b2fc195eSAndrew Gallatin 
2967b2fc195eSAndrew Gallatin static void
29686d87a65dSAndrew Gallatin mxge_init(void *arg)
2969b2fc195eSAndrew Gallatin {
2970b2fc195eSAndrew Gallatin }
2971b2fc195eSAndrew Gallatin 
2972b2fc195eSAndrew Gallatin 
2973b2fc195eSAndrew Gallatin 
2974b2fc195eSAndrew Gallatin static void
29751e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
29761e413cf9SAndrew Gallatin {
29771e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
29781e413cf9SAndrew Gallatin 	int i;
29791e413cf9SAndrew Gallatin 
29801e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
29811e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
29821e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
29831e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
29841e413cf9SAndrew Gallatin 	}
29851e413cf9SAndrew Gallatin 
29861e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
29871e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
29881e413cf9SAndrew Gallatin 			continue;
29891e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
29901e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
29911e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
29921e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
29931e413cf9SAndrew Gallatin 	}
29941e413cf9SAndrew Gallatin 
29951e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
29961e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
29971e413cf9SAndrew Gallatin 			continue;
29981e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
29991e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30001e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30011e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30021e413cf9SAndrew Gallatin 	}
30031e413cf9SAndrew Gallatin 
30041e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30051e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30061e413cf9SAndrew Gallatin 		return;
30071e413cf9SAndrew Gallatin 
30081e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30091e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30101e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30111e413cf9SAndrew Gallatin 			continue;
30121e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30131e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30141e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30151e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30161e413cf9SAndrew Gallatin 	}
30171e413cf9SAndrew Gallatin }
30181e413cf9SAndrew Gallatin 
30191e413cf9SAndrew Gallatin static void
30206d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3021b2fc195eSAndrew Gallatin {
30221e413cf9SAndrew Gallatin 	int slice;
30231e413cf9SAndrew Gallatin 
30241e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
30251e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
30261e413cf9SAndrew Gallatin }
30271e413cf9SAndrew Gallatin 
30281e413cf9SAndrew Gallatin static void
30291e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
30301e413cf9SAndrew Gallatin {
3031b2fc195eSAndrew Gallatin 	int i;
3032b2fc195eSAndrew Gallatin 
3033b2fc195eSAndrew Gallatin 
30341e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
30351e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
30361e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3037b2fc195eSAndrew Gallatin 
30381e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
30391e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
30401e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
30411e413cf9SAndrew Gallatin 
30421e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
30431e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
30441e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
30451e413cf9SAndrew Gallatin 
30461e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
30471e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
30481e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
30491e413cf9SAndrew Gallatin 
30501e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
30511e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
30521e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
30531e413cf9SAndrew Gallatin 
30541e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
30551e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
30561e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
30571e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
30581e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3059b2fc195eSAndrew Gallatin 			}
30601e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
30611e413cf9SAndrew Gallatin 		}
30621e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
30631e413cf9SAndrew Gallatin 	}
30641e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
30651e413cf9SAndrew Gallatin 
30661e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
30671e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
30681e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
30691e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
30701e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
30711e413cf9SAndrew Gallatin 			}
30721e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
30731e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
30741e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
30751e413cf9SAndrew Gallatin 		}
30761e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
30771e413cf9SAndrew Gallatin 	}
30781e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
30791e413cf9SAndrew Gallatin 
30801e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
30811e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
30821e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
30831e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
30841e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
30851e413cf9SAndrew Gallatin 			}
30861e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
30871e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
30881e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
30891e413cf9SAndrew Gallatin 		}
30901e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
30911e413cf9SAndrew Gallatin 	}
30921e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3093b2fc195eSAndrew Gallatin }
3094b2fc195eSAndrew Gallatin 
3095b2fc195eSAndrew Gallatin static void
30966d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3097b2fc195eSAndrew Gallatin {
30981e413cf9SAndrew Gallatin 	int slice;
3099b2fc195eSAndrew Gallatin 
31001e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31011e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3102c2657176SAndrew Gallatin }
3103b2fc195eSAndrew Gallatin 
3104b2fc195eSAndrew Gallatin static int
31051e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31061e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3107b2fc195eSAndrew Gallatin {
31081e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31091e413cf9SAndrew Gallatin 	size_t bytes;
31101e413cf9SAndrew Gallatin 	int err, i;
3111b2fc195eSAndrew Gallatin 
3112b2fc195eSAndrew Gallatin 	err = ENOMEM;
3113b2fc195eSAndrew Gallatin 
31141e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3115adae7080SAndrew Gallatin 
31161e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31171e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3118aed8e389SAndrew Gallatin 
3119b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31201e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31211e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31221e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
31231e413cf9SAndrew Gallatin 		return err;;
3124b2fc195eSAndrew Gallatin 
31251e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31261e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31271e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
31281e413cf9SAndrew Gallatin 		return err;;
3129b2fc195eSAndrew Gallatin 
31301e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
31311e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31321e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31331e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
31341e413cf9SAndrew Gallatin 		return err;;
3135b2fc195eSAndrew Gallatin 
31361e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
31371e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31381e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
31391e413cf9SAndrew Gallatin 		return err;;
3140b2fc195eSAndrew Gallatin 
31411e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3142b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3143b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3144b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3145b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3146b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3147b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3148b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3149b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3150b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3151b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3152b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
31531e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3154b2fc195eSAndrew Gallatin 	if (err != 0) {
3155b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3156b2fc195eSAndrew Gallatin 			      err);
31571e413cf9SAndrew Gallatin 		return err;;
3158b2fc195eSAndrew Gallatin 	}
3159b2fc195eSAndrew Gallatin 
3160b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3161b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3162b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3163b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3164b0f7b922SAndrew Gallatin #else
3165b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3166b0f7b922SAndrew Gallatin #endif
3167b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3168b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3169b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3170053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3171b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3172053e637fSAndrew Gallatin 				 3,			/* num segs */
3173b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
3174b0f7b922SAndrew Gallatin #else
3175b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3176b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3177b0f7b922SAndrew Gallatin #endif
3178b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3179b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
31801e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3181b2fc195eSAndrew Gallatin 	if (err != 0) {
3182b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3183b2fc195eSAndrew Gallatin 			      err);
31841e413cf9SAndrew Gallatin 		return err;;
3185b2fc195eSAndrew Gallatin 	}
31861e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31871e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
31881e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3189b2fc195eSAndrew Gallatin 		if (err != 0) {
3190b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3191b2fc195eSAndrew Gallatin 				      err);
31921e413cf9SAndrew Gallatin 			return err;;
3193b2fc195eSAndrew Gallatin 		}
3194b2fc195eSAndrew Gallatin 	}
31951e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
31961e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3197b2fc195eSAndrew Gallatin 	if (err != 0) {
3198b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3199b2fc195eSAndrew Gallatin 			      err);
32001e413cf9SAndrew Gallatin 		return err;;
3201b2fc195eSAndrew Gallatin 	}
3202b2fc195eSAndrew Gallatin 
32031e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32041e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32051e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3206b2fc195eSAndrew Gallatin 		if (err != 0) {
3207b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3208b2fc195eSAndrew Gallatin 				      err);
32091e413cf9SAndrew Gallatin 			return err;;
3210b2fc195eSAndrew Gallatin 		}
3211b2fc195eSAndrew Gallatin 	}
32121e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32131e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3214b2fc195eSAndrew Gallatin 	if (err != 0) {
3215b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3216b2fc195eSAndrew Gallatin 			      err);
32171e413cf9SAndrew Gallatin 		return err;;
32181e413cf9SAndrew Gallatin 	}
32191e413cf9SAndrew Gallatin 
32201e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
32211e413cf9SAndrew Gallatin 
3222c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
32231e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
32241e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
32251e413cf9SAndrew Gallatin 		return 0;
3226c6cb3e3fSAndrew Gallatin #endif
32271e413cf9SAndrew Gallatin 
32281e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32291e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32301e413cf9SAndrew Gallatin 
32311e413cf9SAndrew Gallatin 
32321e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32331e413cf9SAndrew Gallatin 	bytes = 8 +
32341e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32351e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32361e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
32371e413cf9SAndrew Gallatin 		return err;;
32381e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32391e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
32401e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
32411e413cf9SAndrew Gallatin 
32421e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32431e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32441e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32451e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
32461e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
32471e413cf9SAndrew Gallatin 		return err;;
32481e413cf9SAndrew Gallatin 
32491e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
32501e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
32511e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32521e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
32531e413cf9SAndrew Gallatin 		return err;;
32541e413cf9SAndrew Gallatin 
32551e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
32561e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
32571e413cf9SAndrew Gallatin 				 1,			/* alignment */
32581e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
32591e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
32601e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
32611e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
32621e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
32631e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
32641e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
32651e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
32661e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
32671e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
32681e413cf9SAndrew Gallatin 
32691e413cf9SAndrew Gallatin 	if (err != 0) {
32701e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
32711e413cf9SAndrew Gallatin 			      err);
32721e413cf9SAndrew Gallatin 		return err;;
32731e413cf9SAndrew Gallatin 	}
32741e413cf9SAndrew Gallatin 
32751e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
32761e413cf9SAndrew Gallatin 	   in the ring */
32771e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
32781e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
32791e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
32801e413cf9SAndrew Gallatin 		if (err != 0) {
32811e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
32821e413cf9SAndrew Gallatin 				      err);
32831e413cf9SAndrew Gallatin 			return err;;
32841e413cf9SAndrew Gallatin 		}
3285b2fc195eSAndrew Gallatin 	}
3286b2fc195eSAndrew Gallatin 	return 0;
3287b2fc195eSAndrew Gallatin 
3288b2fc195eSAndrew Gallatin }
3289b2fc195eSAndrew Gallatin 
32901e413cf9SAndrew Gallatin static int
32911e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
32921e413cf9SAndrew Gallatin {
32931e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
32941e413cf9SAndrew Gallatin 	int tx_ring_size;
32951e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
32961e413cf9SAndrew Gallatin 	int err, slice;
32971e413cf9SAndrew Gallatin 
32981e413cf9SAndrew Gallatin 	/* get ring sizes */
32991e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33001e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33011e413cf9SAndrew Gallatin 	if (err != 0) {
33021e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33031e413cf9SAndrew Gallatin 		goto abort;
33041e413cf9SAndrew Gallatin 	}
33051e413cf9SAndrew Gallatin 
33061e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33071e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
33081e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
33091e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
33101e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
33111e413cf9SAndrew Gallatin 
33121e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33131e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33141e413cf9SAndrew Gallatin 					     rx_ring_entries,
33151e413cf9SAndrew Gallatin 					     tx_ring_entries);
33161e413cf9SAndrew Gallatin 		if (err != 0)
33171e413cf9SAndrew Gallatin 			goto abort;
33181e413cf9SAndrew Gallatin 	}
33191e413cf9SAndrew Gallatin 	return 0;
33201e413cf9SAndrew Gallatin 
33211e413cf9SAndrew Gallatin abort:
33221e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33231e413cf9SAndrew Gallatin 	return err;
33241e413cf9SAndrew Gallatin 
33251e413cf9SAndrew Gallatin }
33261e413cf9SAndrew Gallatin 
33271e413cf9SAndrew Gallatin 
3328053e637fSAndrew Gallatin static void
3329053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3330053e637fSAndrew Gallatin {
3331c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3332053e637fSAndrew Gallatin 
3333053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3334053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3335053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3336053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3337053e637fSAndrew Gallatin 		*nbufs = 1;
3338053e637fSAndrew Gallatin 		return;
3339053e637fSAndrew Gallatin 	}
3340053e637fSAndrew Gallatin 
3341053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3342053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3343053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3344053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3345053e637fSAndrew Gallatin 		*nbufs = 1;
3346053e637fSAndrew Gallatin 		return;
3347053e637fSAndrew Gallatin 	}
3348b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3349053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3350053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3351053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3352053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3353053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3354053e637fSAndrew Gallatin 	if (*nbufs == 3)
3355053e637fSAndrew Gallatin 		*nbufs = 4;
3356b0f7b922SAndrew Gallatin #else
3357b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3358b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3359b0f7b922SAndrew Gallatin 	*nbufs = 1;
3360b0f7b922SAndrew Gallatin #endif
3361053e637fSAndrew Gallatin }
3362053e637fSAndrew Gallatin 
3363b2fc195eSAndrew Gallatin static int
33641e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3365b2fc195eSAndrew Gallatin {
33661e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
33676d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3368b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3369053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
33701e413cf9SAndrew Gallatin 	int err, i, slice;
3371b2fc195eSAndrew Gallatin 
33721e413cf9SAndrew Gallatin 
33731e413cf9SAndrew Gallatin 	sc = ss->sc;
33741e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
33751e413cf9SAndrew Gallatin 
33761e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
33771e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3378053e637fSAndrew Gallatin 
3379053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3380053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
33811e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
33821e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3383053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3384053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3385053e637fSAndrew Gallatin 			break;
3386053e637fSAndrew Gallatin 		}
33871e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3388053e637fSAndrew Gallatin 	}
33891e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
33901e413cf9SAndrew Gallatin 
33911e413cf9SAndrew Gallatin 	err = 0;
3392c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
33931e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
33941e413cf9SAndrew Gallatin 	if (slice == 0) {
3395c6cb3e3fSAndrew Gallatin #endif
33961e413cf9SAndrew Gallatin 		cmd.data0 = slice;
33971e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
33981e413cf9SAndrew Gallatin 		ss->tx.lanai =
33991e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3400c6cb3e3fSAndrew Gallatin 		ss->tx.send_go = (volatile uint32_t *)
3401c6cb3e3fSAndrew Gallatin 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3402c6cb3e3fSAndrew Gallatin 		ss->tx.send_stop = (volatile uint32_t *)
3403c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3404c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
34051e413cf9SAndrew Gallatin 	}
3406c6cb3e3fSAndrew Gallatin #endif
34071e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34081e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34091e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34101e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34111e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34121e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34131e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34141e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34151e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34161e413cf9SAndrew Gallatin 
34171e413cf9SAndrew Gallatin 	if (err != 0) {
34181e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34191e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34201e413cf9SAndrew Gallatin 		return EIO;
34211e413cf9SAndrew Gallatin 	}
34221e413cf9SAndrew Gallatin 
34231e413cf9SAndrew Gallatin 	/* stock receive rings */
34241e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34251e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34261e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34271e413cf9SAndrew Gallatin 		if (err) {
34281e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34291e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34301e413cf9SAndrew Gallatin 			return ENOMEM;
34311e413cf9SAndrew Gallatin 		}
34321e413cf9SAndrew Gallatin 	}
34331e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34341e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34351e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34361e413cf9SAndrew Gallatin 	}
34371e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34381e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
34394d9a5852SAndrew Gallatin 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
34404d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34411e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34421e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34431e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34441e413cf9SAndrew Gallatin 		if (err) {
34451e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34461e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
34471e413cf9SAndrew Gallatin 			return ENOMEM;
34481e413cf9SAndrew Gallatin 		}
34491e413cf9SAndrew Gallatin 	}
34501e413cf9SAndrew Gallatin 	return 0;
34511e413cf9SAndrew Gallatin }
34521e413cf9SAndrew Gallatin 
34531e413cf9SAndrew Gallatin static int
34541e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
34551e413cf9SAndrew Gallatin {
34561e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34571e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
34581e413cf9SAndrew Gallatin 	bus_addr_t bus;
34591e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3460c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3461b2fc195eSAndrew Gallatin 
34627d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
34637d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34647d542e2dSAndrew Gallatin 
3465adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3466b2fc195eSAndrew Gallatin 	if (err != 0) {
3467b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3468b2fc195eSAndrew Gallatin 		return EIO;
3469b2fc195eSAndrew Gallatin 	}
3470b2fc195eSAndrew Gallatin 
34711e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
34721e413cf9SAndrew Gallatin 		/* setup the indirection table */
34731e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
34741e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34751e413cf9SAndrew Gallatin 				    &cmd);
3476b2fc195eSAndrew Gallatin 
34771e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34781e413cf9SAndrew Gallatin 				     &cmd);
34791e413cf9SAndrew Gallatin 		if (err != 0) {
34801e413cf9SAndrew Gallatin 			device_printf(sc->dev,
34811e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
34821e413cf9SAndrew Gallatin 			return err;
34831e413cf9SAndrew Gallatin 		}
34841e413cf9SAndrew Gallatin 
34851e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
34861e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
34871e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
34881e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
34891e413cf9SAndrew Gallatin 
34901e413cf9SAndrew Gallatin 		cmd.data0 = 1;
34911e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
34921e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
34931e413cf9SAndrew Gallatin 		if (err != 0) {
34941e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
34951e413cf9SAndrew Gallatin 			return err;
34961e413cf9SAndrew Gallatin 		}
34971e413cf9SAndrew Gallatin 	}
34981e413cf9SAndrew Gallatin 
34991e413cf9SAndrew Gallatin 
35001e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35011e413cf9SAndrew Gallatin 
35021e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3503053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3504053e637fSAndrew Gallatin 			    &cmd);
3505053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3506053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35071e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3508053e637fSAndrew Gallatin 		device_printf(sc->dev,
3509053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35101e413cf9SAndrew Gallatin 			      nbufs);
3511053e637fSAndrew Gallatin 		return EIO;
3512053e637fSAndrew Gallatin 	}
3513b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3514b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3515b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3516c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35175e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3518b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35195e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3520b2fc195eSAndrew Gallatin 			     &cmd);
3521053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35225e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35230fa7f681SAndrew Gallatin 
35240fa7f681SAndrew Gallatin 	if (err != 0) {
35250fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35260fa7f681SAndrew Gallatin 		goto abort;
35270fa7f681SAndrew Gallatin 	}
35280fa7f681SAndrew Gallatin 
3529b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
3530c6cb3e3fSAndrew Gallatin 	for (slice = 0;
3531c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3532c6cb3e3fSAndrew Gallatin 	     slice < sc->num_slices;
3533c6cb3e3fSAndrew Gallatin #else
3534c6cb3e3fSAndrew Gallatin 	     slice < 1;
3535c6cb3e3fSAndrew Gallatin #endif
3536c6cb3e3fSAndrew Gallatin 	     slice++) {
3537c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3538c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3539c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3540c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3541c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35420fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3543c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3544c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3545c6cb3e3fSAndrew Gallatin 	}
35460fa7f681SAndrew Gallatin 
35470fa7f681SAndrew Gallatin 	if (err != 0) {
35481e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
35490fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
35500fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35510fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35520fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
35530fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35540fa7f681SAndrew Gallatin 				    &cmd);
35550fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
35560fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
35570fa7f681SAndrew Gallatin 	} else {
35580fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
35590fa7f681SAndrew Gallatin 	}
3560b2fc195eSAndrew Gallatin 
3561b2fc195eSAndrew Gallatin 	if (err != 0) {
3562b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3563b2fc195eSAndrew Gallatin 		goto abort;
3564b2fc195eSAndrew Gallatin 	}
3565b2fc195eSAndrew Gallatin 
35661e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
35671e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35681e413cf9SAndrew Gallatin 		if (err != 0) {
35691e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
35701e413cf9SAndrew Gallatin 				      slice);
35711e413cf9SAndrew Gallatin 			goto abort;
35721e413cf9SAndrew Gallatin 		}
35731e413cf9SAndrew Gallatin 	}
35741e413cf9SAndrew Gallatin 
3575b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
35765e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3577b2fc195eSAndrew Gallatin 	if (err) {
3578b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3579b2fc195eSAndrew Gallatin 		goto abort;
3580b2fc195eSAndrew Gallatin 	}
3581c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3582c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3583c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3584c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3585c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3586c6cb3e3fSAndrew Gallatin 	}
3587c6cb3e3fSAndrew Gallatin #endif
3588b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3589b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3590e749ef6bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3591b2fc195eSAndrew Gallatin 
3592b2fc195eSAndrew Gallatin 	return 0;
3593b2fc195eSAndrew Gallatin 
3594b2fc195eSAndrew Gallatin 
3595b2fc195eSAndrew Gallatin abort:
35966d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3597a98d6cd7SAndrew Gallatin 
3598b2fc195eSAndrew Gallatin 	return err;
3599b2fc195eSAndrew Gallatin }
3600b2fc195eSAndrew Gallatin 
3601b2fc195eSAndrew Gallatin static int
36026d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3603b2fc195eSAndrew Gallatin {
36046d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3605b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3606c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3607c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3608c6cb3e3fSAndrew Gallatin 	int slice;
3609c6cb3e3fSAndrew Gallatin #endif
3610b2fc195eSAndrew Gallatin 
3611e749ef6bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3612c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3613c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3614c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3615c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3616c6cb3e3fSAndrew Gallatin 	}
3617c6cb3e3fSAndrew Gallatin #endif
3618b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3619b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
362073c7c83fSAndrew Gallatin 	wmb();
36215e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3622b2fc195eSAndrew Gallatin 	if (err) {
3623b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3624b2fc195eSAndrew Gallatin 	}
3625b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3626b2fc195eSAndrew Gallatin 		/* wait for down irq */
3627dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3628b2fc195eSAndrew Gallatin 	}
362973c7c83fSAndrew Gallatin 	wmb();
3630b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3631b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3632b2fc195eSAndrew Gallatin 	}
3633a98d6cd7SAndrew Gallatin 
36346d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3635a98d6cd7SAndrew Gallatin 
3636b2fc195eSAndrew Gallatin 	return 0;
3637b2fc195eSAndrew Gallatin }
3638b2fc195eSAndrew Gallatin 
3639dce01b9bSAndrew Gallatin static void
3640dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3641dce01b9bSAndrew Gallatin {
3642dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3643dce01b9bSAndrew Gallatin 	int reg;
3644dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3645dce01b9bSAndrew Gallatin 
3646dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3647dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3648dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3649dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3650dce01b9bSAndrew Gallatin 
3651dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3652dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3653dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3654dce01b9bSAndrew Gallatin 	}
3655dce01b9bSAndrew Gallatin 
3656dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3657dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3658dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3659dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3660dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3661dce01b9bSAndrew Gallatin }
3662dce01b9bSAndrew Gallatin 
3663dce01b9bSAndrew Gallatin static uint32_t
3664dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3665dce01b9bSAndrew Gallatin {
3666dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3667dce01b9bSAndrew Gallatin 	uint32_t vs;
3668dce01b9bSAndrew Gallatin 
3669dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3670dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3671dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3672dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3673dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3674dce01b9bSAndrew Gallatin 	}
3675dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3676dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3677dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3678dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3679dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3680dce01b9bSAndrew Gallatin }
3681dce01b9bSAndrew Gallatin 
3682e749ef6bSAndrew Gallatin static int
3683c6cb3e3fSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc, int slice)
3684dce01b9bSAndrew Gallatin {
3685e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3686c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
3687dce01b9bSAndrew Gallatin 	int err;
3688dce01b9bSAndrew Gallatin 	uint32_t reboot;
3689dce01b9bSAndrew Gallatin 	uint16_t cmd;
3690dce01b9bSAndrew Gallatin 
3691dce01b9bSAndrew Gallatin 	err = ENXIO;
3692dce01b9bSAndrew Gallatin 
3693dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3694dce01b9bSAndrew Gallatin 
3695dce01b9bSAndrew Gallatin 	/*
3696dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3697dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3698dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3699dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3700dce01b9bSAndrew Gallatin 	 * again
3701dce01b9bSAndrew Gallatin 	 */
3702dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3703dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3704dce01b9bSAndrew Gallatin 		/*
3705dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3706dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3707dce01b9bSAndrew Gallatin 		 * back, then give up
3708dce01b9bSAndrew Gallatin 		 */
3709dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3710dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3711dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3712dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3713e749ef6bSAndrew Gallatin 			return (err);
3714dce01b9bSAndrew Gallatin 		}
3715dce01b9bSAndrew Gallatin 	}
3716dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3717dce01b9bSAndrew Gallatin 		/* print the reboot status */
3718dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3719dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3720dce01b9bSAndrew Gallatin 			      reboot);
3721dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3722e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3723e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3724dce01b9bSAndrew Gallatin 
3725dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3726dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
372710882804SAndrew Gallatin 
372810882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
372910882804SAndrew Gallatin 			mxge_close(sc);
373010882804SAndrew Gallatin 			err = mxge_open(sc);
373110882804SAndrew Gallatin 		}
3732dce01b9bSAndrew Gallatin 	} else {
3733c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
3734c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3735c6cb3e3fSAndrew Gallatin 			      "NIC did not reboot, slice %d ring state:\n",
3736c6cb3e3fSAndrew Gallatin 			      slice);
3737c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
3738c6cb3e3fSAndrew Gallatin 			      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3739c6cb3e3fSAndrew Gallatin 			      tx->req, tx->done, tx->queue_active);
3740c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3741c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3742dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3743c6cb3e3fSAndrew Gallatin 			      tx->pkt_done,
37441e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
374510882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3746dce01b9bSAndrew Gallatin 	}
3747e749ef6bSAndrew Gallatin 	return (err);
3748dce01b9bSAndrew Gallatin }
3749dce01b9bSAndrew Gallatin 
3750e749ef6bSAndrew Gallatin static int
3751dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3752dce01b9bSAndrew Gallatin {
3753c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
37541e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3755c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3756dce01b9bSAndrew Gallatin 
3757dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3758dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3759c6cb3e3fSAndrew Gallatin 	for (i = 0;
3760c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
3761c6cb3e3fSAndrew Gallatin 	     (i < sc->num_slices) && (err == 0);
3762c6cb3e3fSAndrew Gallatin #else
3763c6cb3e3fSAndrew Gallatin 	     (i < 1) && (err == 0);
3764c6cb3e3fSAndrew Gallatin #endif
3765c6cb3e3fSAndrew Gallatin 	     i++) {
3766c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3767dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3768dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3769c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3770c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
3771c587e59fSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause)
3772c6cb3e3fSAndrew Gallatin 				err = mxge_watchdog_reset(sc, i);
3773c587e59fSAndrew Gallatin 			else
3774c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3775c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3776c587e59fSAndrew Gallatin 		}
3777dce01b9bSAndrew Gallatin 
3778dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3779dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3780c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3781c6cb3e3fSAndrew Gallatin 	}
3782c587e59fSAndrew Gallatin 
3783c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3784c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3785e749ef6bSAndrew Gallatin 	return (err);
3786dce01b9bSAndrew Gallatin }
3787dce01b9bSAndrew Gallatin 
3788dce01b9bSAndrew Gallatin static void
37891e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
37901e413cf9SAndrew Gallatin {
37911e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37921e413cf9SAndrew Gallatin 	u_long ipackets = 0;
3793c6cb3e3fSAndrew Gallatin 	u_long opackets = 0;
379471032832SAndrew Gallatin #ifdef IFNET_BUF_RING
379571032832SAndrew Gallatin 	u_long obytes = 0;
379671032832SAndrew Gallatin 	u_long omcasts = 0;
379771032832SAndrew Gallatin 	u_long odrops = 0;
379871032832SAndrew Gallatin #endif
3799c6cb3e3fSAndrew Gallatin 	u_long oerrors = 0;
38001e413cf9SAndrew Gallatin 	int slice;
38011e413cf9SAndrew Gallatin 
38021e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
38031e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
38041e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
3805c6cb3e3fSAndrew Gallatin 		opackets += ss->opackets;
380671032832SAndrew Gallatin #ifdef IFNET_BUF_RING
380771032832SAndrew Gallatin 		obytes += ss->obytes;
380871032832SAndrew Gallatin 		omcasts += ss->omcasts;
380971032832SAndrew Gallatin 		odrops += ss->tx.br->br_drops;
381071032832SAndrew Gallatin #endif
3811c6cb3e3fSAndrew Gallatin 		oerrors += ss->oerrors;
38121e413cf9SAndrew Gallatin 	}
38131e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
3814c6cb3e3fSAndrew Gallatin 	sc->ifp->if_opackets = opackets;
381571032832SAndrew Gallatin #ifdef IFNET_BUF_RING
381671032832SAndrew Gallatin 	sc->ifp->if_obytes = obytes;
381771032832SAndrew Gallatin 	sc->ifp->if_omcasts = omcasts;
381871032832SAndrew Gallatin 	sc->ifp->if_snd.ifq_drops = odrops;
381971032832SAndrew Gallatin #endif
3820c6cb3e3fSAndrew Gallatin 	sc->ifp->if_oerrors = oerrors;
38211e413cf9SAndrew Gallatin }
3822c6cb3e3fSAndrew Gallatin 
38231e413cf9SAndrew Gallatin static void
3824dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3825dce01b9bSAndrew Gallatin {
3826dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3827e749ef6bSAndrew Gallatin 	int err = 0;
3828dce01b9bSAndrew Gallatin 
38291e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
38301e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
38311e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3832e749ef6bSAndrew Gallatin 		err = mxge_watchdog(sc);
38331e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
38341e413cf9SAndrew Gallatin 	}
38351e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3836e749ef6bSAndrew Gallatin 	if (err == 0)
3837e749ef6bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3838e749ef6bSAndrew Gallatin 
3839dce01b9bSAndrew Gallatin }
3840b2fc195eSAndrew Gallatin 
3841b2fc195eSAndrew Gallatin static int
38426d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3843b2fc195eSAndrew Gallatin {
3844b2fc195eSAndrew Gallatin 	return EINVAL;
3845b2fc195eSAndrew Gallatin }
3846b2fc195eSAndrew Gallatin 
3847b2fc195eSAndrew Gallatin static int
38486d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3849b2fc195eSAndrew Gallatin {
3850b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3851b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3852b2fc195eSAndrew Gallatin 	int err = 0;
3853b2fc195eSAndrew Gallatin 
3854b2fc195eSAndrew Gallatin 
3855c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3856053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3857b2fc195eSAndrew Gallatin 		return EINVAL;
3858a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3859b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3860b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3861b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
38626d87a65dSAndrew Gallatin 		mxge_close(sc);
38636d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3864b2fc195eSAndrew Gallatin 		if (err != 0) {
3865b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
38666d87a65dSAndrew Gallatin 			mxge_close(sc);
38676d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3868b2fc195eSAndrew Gallatin 		}
3869b2fc195eSAndrew Gallatin 	}
3870a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3871b2fc195eSAndrew Gallatin 	return err;
3872b2fc195eSAndrew Gallatin }
3873b2fc195eSAndrew Gallatin 
3874b2fc195eSAndrew Gallatin static void
38756d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3876b2fc195eSAndrew Gallatin {
38776d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3878b2fc195eSAndrew Gallatin 
3879b2fc195eSAndrew Gallatin 
3880b2fc195eSAndrew Gallatin 	if (sc == NULL)
3881b2fc195eSAndrew Gallatin 		return;
3882b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3883c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3884b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3885c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3886b2fc195eSAndrew Gallatin }
3887b2fc195eSAndrew Gallatin 
3888b2fc195eSAndrew Gallatin static int
38896d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3890b2fc195eSAndrew Gallatin {
38916d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3892b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3893b2fc195eSAndrew Gallatin 	int err, mask;
3894b2fc195eSAndrew Gallatin 
3895b2fc195eSAndrew Gallatin 	err = 0;
3896b2fc195eSAndrew Gallatin 	switch (command) {
3897b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3898b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3899b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3900b2fc195eSAndrew Gallatin 		break;
3901b2fc195eSAndrew Gallatin 
3902b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
39036d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3904b2fc195eSAndrew Gallatin 		break;
3905b2fc195eSAndrew Gallatin 
3906b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3907a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3908b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3909dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
39106d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3911dce01b9bSAndrew Gallatin 			} else {
39120fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
39130fa7f681SAndrew Gallatin 				   flag chages */
39140fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
39150fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
39160fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
39170fa7f681SAndrew Gallatin 			}
3918b2fc195eSAndrew Gallatin 		} else {
3919dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
39201e413cf9SAndrew Gallatin 				mxge_close(sc);
3921dce01b9bSAndrew Gallatin 			}
3922b2fc195eSAndrew Gallatin 		}
3923a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3924b2fc195eSAndrew Gallatin 		break;
3925b2fc195eSAndrew Gallatin 
3926b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3927b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3928a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
39290fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3930a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3931b2fc195eSAndrew Gallatin 		break;
3932b2fc195eSAndrew Gallatin 
3933b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3934a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3935b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3936b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3937b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3938aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3939aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3940aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3941b2fc195eSAndrew Gallatin 			} else {
3942b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3943b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3944b2fc195eSAndrew Gallatin 			}
3945b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3946b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3947b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
39485e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3949b2fc195eSAndrew Gallatin 			} else {
3950b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
39515e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3952b2fc195eSAndrew Gallatin 			}
3953b2fc195eSAndrew Gallatin 		}
3954aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3955aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3956aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3957aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3958aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3959aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3960aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3961aed8e389SAndrew Gallatin 			} else {
3962aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3963aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3964aed8e389SAndrew Gallatin 				err = EINVAL;
3965aed8e389SAndrew Gallatin 			}
3966aed8e389SAndrew Gallatin 		}
3967f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3968f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3969f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3970f04b33f8SAndrew Gallatin 			else
3971f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3972f04b33f8SAndrew Gallatin 		}
3973c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3974c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3975a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3976c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3977c792928fSAndrew Gallatin 
3978b2fc195eSAndrew Gallatin 		break;
3979b2fc195eSAndrew Gallatin 
3980b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3981b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3982b2fc195eSAndrew Gallatin 				    &sc->media, command);
3983b2fc195eSAndrew Gallatin                 break;
3984b2fc195eSAndrew Gallatin 
3985b2fc195eSAndrew Gallatin 	default:
3986b2fc195eSAndrew Gallatin 		err = ENOTTY;
3987b2fc195eSAndrew Gallatin         }
3988b2fc195eSAndrew Gallatin 	return err;
3989b2fc195eSAndrew Gallatin }
3990b2fc195eSAndrew Gallatin 
3991b2fc195eSAndrew Gallatin static void
39926d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3993b2fc195eSAndrew Gallatin {
3994b2fc195eSAndrew Gallatin 
39951e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
39966d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
39976d87a65dSAndrew Gallatin 			  &mxge_flow_control);
39986d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
39996d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
40006d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
40016d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4002d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4003d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
40045e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
40055e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
40065e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
40075e7d8541SAndrew Gallatin 			  &mxge_verbose);
4008dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
4009053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
40101e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
40111e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
4012f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
4013f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
4014b2fc195eSAndrew Gallatin 
40155e7d8541SAndrew Gallatin 	if (bootverbose)
40165e7d8541SAndrew Gallatin 		mxge_verbose = 1;
40176d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
40186d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4019dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
40201e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
40216d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
40221e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4023bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
40241e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
4025b2fc195eSAndrew Gallatin 	}
40261e413cf9SAndrew Gallatin }
40271e413cf9SAndrew Gallatin 
40281e413cf9SAndrew Gallatin 
40291e413cf9SAndrew Gallatin static void
40301e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
40311e413cf9SAndrew Gallatin {
40321e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
40331e413cf9SAndrew Gallatin 	int i;
40341e413cf9SAndrew Gallatin 
40351e413cf9SAndrew Gallatin 
40361e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
40371e413cf9SAndrew Gallatin 		return;
40381e413cf9SAndrew Gallatin 
40391e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40401e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
40411e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
40421e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
40431e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4044c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4045c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4046c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4047c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4048c6cb3e3fSAndrew Gallatin 			}
4049c6cb3e3fSAndrew Gallatin #endif
40501e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
40511e413cf9SAndrew Gallatin 		}
40521e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
40531e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
40541e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
40551e413cf9SAndrew Gallatin 		}
40561e413cf9SAndrew Gallatin 	}
40571e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
40581e413cf9SAndrew Gallatin 	sc->ss = NULL;
40591e413cf9SAndrew Gallatin }
40601e413cf9SAndrew Gallatin 
40611e413cf9SAndrew Gallatin static int
40621e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
40631e413cf9SAndrew Gallatin {
40641e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
40651e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
40661e413cf9SAndrew Gallatin 	size_t bytes;
40671e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
40681e413cf9SAndrew Gallatin 
40691e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40701e413cf9SAndrew Gallatin 	if (err != 0) {
40711e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
40721e413cf9SAndrew Gallatin 		return err;
40731e413cf9SAndrew Gallatin 	}
40741e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
40751e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
40761e413cf9SAndrew Gallatin 
40771e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
40781e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
40791e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
40801e413cf9SAndrew Gallatin 		return (ENOMEM);
40811e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40821e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
40831e413cf9SAndrew Gallatin 
40841e413cf9SAndrew Gallatin 		ss->sc = sc;
40851e413cf9SAndrew Gallatin 
40861e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
40871e413cf9SAndrew Gallatin 
40881e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
40891e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
40901e413cf9SAndrew Gallatin 		if (err != 0)
40911e413cf9SAndrew Gallatin 			goto abort;
40921e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
40931e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
40941e413cf9SAndrew Gallatin 
40951e413cf9SAndrew Gallatin 		/*
40961e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
40971e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
40981e413cf9SAndrew Gallatin 		 * slice for now
40991e413cf9SAndrew Gallatin 		 */
4100c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING
41011e413cf9SAndrew Gallatin 		if (i > 0)
41021e413cf9SAndrew Gallatin 			continue;
4103c6cb3e3fSAndrew Gallatin #endif
41041e413cf9SAndrew Gallatin 
41051e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
41061e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
41071e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
41081e413cf9SAndrew Gallatin 		if (err != 0)
41091e413cf9SAndrew Gallatin 			goto abort;
41101e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
41111e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
41121e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
41131e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4114c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4115c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4116c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
4117c6cb3e3fSAndrew Gallatin #endif
41181e413cf9SAndrew Gallatin 	}
41191e413cf9SAndrew Gallatin 
41201e413cf9SAndrew Gallatin 	return (0);
41211e413cf9SAndrew Gallatin 
41221e413cf9SAndrew Gallatin abort:
41231e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
41241e413cf9SAndrew Gallatin 	return (ENOMEM);
41251e413cf9SAndrew Gallatin }
41261e413cf9SAndrew Gallatin 
41271e413cf9SAndrew Gallatin static void
41281e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
41291e413cf9SAndrew Gallatin {
41301e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
41311e413cf9SAndrew Gallatin 	char *old_fw;
41321e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
41331e413cf9SAndrew Gallatin 
41341e413cf9SAndrew Gallatin 	sc->num_slices = 1;
41351e413cf9SAndrew Gallatin 	/*
41361e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
41371e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
41381e413cf9SAndrew Gallatin 	 */
41391e413cf9SAndrew Gallatin 
41401e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
41411e413cf9SAndrew Gallatin 		return;
41421e413cf9SAndrew Gallatin 
41431e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
41441e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
41451e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
41461e413cf9SAndrew Gallatin 		return;
41471e413cf9SAndrew Gallatin 
41481e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
41491e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
41501e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
41511e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
41521e413cf9SAndrew Gallatin 	else
41531e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
41541e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
41551e413cf9SAndrew Gallatin 	if (status != 0) {
41561e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
41571e413cf9SAndrew Gallatin 		return;
41581e413cf9SAndrew Gallatin 	}
41591e413cf9SAndrew Gallatin 
41601e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
41611e413cf9SAndrew Gallatin 	   is alive */
41621e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
41631e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
41641e413cf9SAndrew Gallatin 	if (status != 0) {
41651e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
41661e413cf9SAndrew Gallatin 		goto abort_with_fw;
41671e413cf9SAndrew Gallatin 	}
41681e413cf9SAndrew Gallatin 
41691e413cf9SAndrew Gallatin 	/* get rx ring size */
41701e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
41711e413cf9SAndrew Gallatin 	if (status != 0) {
41721e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
41731e413cf9SAndrew Gallatin 		goto abort_with_fw;
41741e413cf9SAndrew Gallatin 	}
41751e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
41761e413cf9SAndrew Gallatin 
41771e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
41781e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
41791e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
41801e413cf9SAndrew Gallatin 	if (status != 0) {
41811e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
41821e413cf9SAndrew Gallatin 		goto abort_with_fw;
41831e413cf9SAndrew Gallatin 	}
41841e413cf9SAndrew Gallatin 
41851e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
41861e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
41871e413cf9SAndrew Gallatin 	if (status != 0) {
41881e413cf9SAndrew Gallatin 		device_printf(sc->dev,
41891e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
41901e413cf9SAndrew Gallatin 		goto abort_with_fw;
41911e413cf9SAndrew Gallatin 	}
41921e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
41931e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
41941e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
41951e413cf9SAndrew Gallatin 
41961e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
41971e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
41981e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
41991e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
42001e413cf9SAndrew Gallatin 	} else {
42011e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
42021e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
42031e413cf9SAndrew Gallatin 	}
42041e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
42051e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
42061e413cf9SAndrew Gallatin 		sc->num_slices--;
42071e413cf9SAndrew Gallatin 
42081e413cf9SAndrew Gallatin 	if (mxge_verbose)
42091e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
42101e413cf9SAndrew Gallatin 			      sc->num_slices);
42111e413cf9SAndrew Gallatin 
42121e413cf9SAndrew Gallatin 	return;
42131e413cf9SAndrew Gallatin 
42141e413cf9SAndrew Gallatin abort_with_fw:
42151e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
42161e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
42171e413cf9SAndrew Gallatin }
42181e413cf9SAndrew Gallatin 
42191e413cf9SAndrew Gallatin static int
42201e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
42211e413cf9SAndrew Gallatin {
42221e413cf9SAndrew Gallatin 	size_t bytes;
42231e413cf9SAndrew Gallatin 	int count, err, i, rid;
42241e413cf9SAndrew Gallatin 
42251e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
42261e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
42271e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
42281e413cf9SAndrew Gallatin 
42291e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
42301e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
42311e413cf9SAndrew Gallatin 		return ENXIO;
42321e413cf9SAndrew Gallatin 	}
42331e413cf9SAndrew Gallatin 
42341e413cf9SAndrew Gallatin 	count = sc->num_slices;
42351e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
42361e413cf9SAndrew Gallatin 	if (err != 0) {
42371e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
42381e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
42391e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
42401e413cf9SAndrew Gallatin 	}
42411e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
42421e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
42431e413cf9SAndrew Gallatin 			      count, sc->num_slices);
42441e413cf9SAndrew Gallatin 		device_printf(sc->dev,
42451e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
42461e413cf9SAndrew Gallatin 			      count);
42471e413cf9SAndrew Gallatin 		err = ENOSPC;
42481e413cf9SAndrew Gallatin 		goto abort_with_msix;
42491e413cf9SAndrew Gallatin 	}
42501e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
42511e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42521e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
42531e413cf9SAndrew Gallatin 		err = ENOMEM;
42541e413cf9SAndrew Gallatin 		goto abort_with_msix;
42551e413cf9SAndrew Gallatin 	}
42561e413cf9SAndrew Gallatin 
42571e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42581e413cf9SAndrew Gallatin 		rid = i + 1;
42591e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
42601e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
42611e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
42621e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
42631e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
42641e413cf9SAndrew Gallatin 				      " for message %d\n", i);
42651e413cf9SAndrew Gallatin 			err = ENXIO;
42661e413cf9SAndrew Gallatin 			goto abort_with_res;
42671e413cf9SAndrew Gallatin 		}
42681e413cf9SAndrew Gallatin 	}
42691e413cf9SAndrew Gallatin 
42701e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
42711e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42721e413cf9SAndrew Gallatin 
42731e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42741e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
42751e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
427637d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
427737d89b0cSAndrew Gallatin 				     NULL,
427837d89b0cSAndrew Gallatin #endif
427937d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
42801e413cf9SAndrew Gallatin 		if (err != 0) {
42811e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
42821e413cf9SAndrew Gallatin 				      "message %d\n", i);
42831e413cf9SAndrew Gallatin 			goto abort_with_intr;
42841e413cf9SAndrew Gallatin 		}
42851e413cf9SAndrew Gallatin 	}
42861e413cf9SAndrew Gallatin 
42871e413cf9SAndrew Gallatin 	if (mxge_verbose) {
42881e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
42891e413cf9SAndrew Gallatin 			      sc->num_slices);
42901e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
42911e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
42921e413cf9SAndrew Gallatin 		printf("\n");
42931e413cf9SAndrew Gallatin 	}
42941e413cf9SAndrew Gallatin 	return (0);
42951e413cf9SAndrew Gallatin 
42961e413cf9SAndrew Gallatin abort_with_intr:
42971e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42981e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
42991e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43001e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
43011e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
43021e413cf9SAndrew Gallatin 		}
43031e413cf9SAndrew Gallatin 	}
43041e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
43051e413cf9SAndrew Gallatin 
43061e413cf9SAndrew Gallatin 
43071e413cf9SAndrew Gallatin abort_with_res:
43081e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43091e413cf9SAndrew Gallatin 		rid = i + 1;
43101e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
43111e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43121e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
43131e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
43141e413cf9SAndrew Gallatin 	}
43151e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
43161e413cf9SAndrew Gallatin 
43171e413cf9SAndrew Gallatin 
43181e413cf9SAndrew Gallatin abort_with_msix:
43191e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
43201e413cf9SAndrew Gallatin 
43211e413cf9SAndrew Gallatin abort_with_msix_table:
43221e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43231e413cf9SAndrew Gallatin 			     sc->msix_table_res);
43241e413cf9SAndrew Gallatin 
43251e413cf9SAndrew Gallatin 	return err;
43261e413cf9SAndrew Gallatin }
43271e413cf9SAndrew Gallatin 
43281e413cf9SAndrew Gallatin static int
43291e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
43301e413cf9SAndrew Gallatin {
43311e413cf9SAndrew Gallatin 	int count, err, rid;
43321e413cf9SAndrew Gallatin 
43331e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
43341e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
43351e413cf9SAndrew Gallatin 		rid = 1;
43361e413cf9SAndrew Gallatin 	} else {
43371e413cf9SAndrew Gallatin 		rid = 0;
433891ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
43391e413cf9SAndrew Gallatin 	}
43401e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
43411e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
43421e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
43431e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
43441e413cf9SAndrew Gallatin 		return ENXIO;
43451e413cf9SAndrew Gallatin 	}
43461e413cf9SAndrew Gallatin 	if (mxge_verbose)
43471e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
434891ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
43491e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
43501e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
43511e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
435237d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
435337d89b0cSAndrew Gallatin 			     NULL,
435437d89b0cSAndrew Gallatin #endif
435537d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
43561e413cf9SAndrew Gallatin 	if (err != 0) {
43571e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
435891ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
435991ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
43601e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
43611e413cf9SAndrew Gallatin 	}
43621e413cf9SAndrew Gallatin 	return err;
43631e413cf9SAndrew Gallatin }
43641e413cf9SAndrew Gallatin 
43651e413cf9SAndrew Gallatin static void
43661e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
43671e413cf9SAndrew Gallatin {
43681e413cf9SAndrew Gallatin 	int i, rid;
43691e413cf9SAndrew Gallatin 
43701e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43711e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
43721e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43731e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
43741e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
43751e413cf9SAndrew Gallatin 		}
43761e413cf9SAndrew Gallatin 	}
43771e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
43781e413cf9SAndrew Gallatin 
43791e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
43801e413cf9SAndrew Gallatin 		rid = i + 1;
43811e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
43821e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43831e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
43841e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
43851e413cf9SAndrew Gallatin 	}
43861e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
43871e413cf9SAndrew Gallatin 
43881e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43891e413cf9SAndrew Gallatin 			     sc->msix_table_res);
43901e413cf9SAndrew Gallatin 
43911e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
43921e413cf9SAndrew Gallatin 	return;
43931e413cf9SAndrew Gallatin }
43941e413cf9SAndrew Gallatin 
43951e413cf9SAndrew Gallatin static void
43961e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
43971e413cf9SAndrew Gallatin {
43981e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
43991e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
440091ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
440191ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
44021e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
44031e413cf9SAndrew Gallatin }
44041e413cf9SAndrew Gallatin 
44051e413cf9SAndrew Gallatin static void
44061e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
44071e413cf9SAndrew Gallatin {
44081e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44091e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44101e413cf9SAndrew Gallatin 	else
44111e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
44121e413cf9SAndrew Gallatin }
44131e413cf9SAndrew Gallatin 
44141e413cf9SAndrew Gallatin static int
44151e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
44161e413cf9SAndrew Gallatin {
44171e413cf9SAndrew Gallatin 	int err;
44181e413cf9SAndrew Gallatin 
44191e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
44201e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44211e413cf9SAndrew Gallatin 	else
44221e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
44231e413cf9SAndrew Gallatin 
44241e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
44251e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
44261e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
44271e413cf9SAndrew Gallatin 	}
44281e413cf9SAndrew Gallatin 	return err;
44291e413cf9SAndrew Gallatin }
44301e413cf9SAndrew Gallatin 
4431b2fc195eSAndrew Gallatin 
4432b2fc195eSAndrew Gallatin static int
44336d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4434b2fc195eSAndrew Gallatin {
44356d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4436b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
44371e413cf9SAndrew Gallatin 	int err, rid;
4438b2fc195eSAndrew Gallatin 
4439b2fc195eSAndrew Gallatin 	sc->dev = dev;
44406d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4441b2fc195eSAndrew Gallatin 
4442b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4443b2fc195eSAndrew Gallatin 				 1,			/* alignment */
44441e413cf9SAndrew Gallatin 				 0,			/* boundary */
4445b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4446b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4447b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4448aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
44495e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
44501e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4451b2fc195eSAndrew Gallatin 				 0,			/* flags */
4452b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4453b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4454b2fc195eSAndrew Gallatin 
4455b2fc195eSAndrew Gallatin 	if (err != 0) {
4456b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4457b2fc195eSAndrew Gallatin 			      err);
4458b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4459b2fc195eSAndrew Gallatin 	}
4460b2fc195eSAndrew Gallatin 
4461b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4462b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4463b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4464b2fc195eSAndrew Gallatin 		err = ENOSPC;
4465b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4466b2fc195eSAndrew Gallatin 	}
44671e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
44681e413cf9SAndrew Gallatin 
4469a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4470a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4471a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4472a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4473a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4474a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4475b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4476b2fc195eSAndrew Gallatin 
4477dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4478d91b1b49SAndrew Gallatin 
4479dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4480b2fc195eSAndrew Gallatin 
4481b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4482b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4483b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4484b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4485b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4486b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4487b2fc195eSAndrew Gallatin 		err = ENXIO;
4488b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4489b2fc195eSAndrew Gallatin 	}
4490b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4491b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4492b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4493b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4494b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4495b2fc195eSAndrew Gallatin 		err = ENXIO;
4496b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4497b2fc195eSAndrew Gallatin 	}
4498b2fc195eSAndrew Gallatin 
4499b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4500b2fc195eSAndrew Gallatin 	   lanai SRAM */
45016d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4502b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4503b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
45046d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4505b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
45066d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
45076d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4508b2fc195eSAndrew Gallatin 	if (err != 0)
4509b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4510b2fc195eSAndrew Gallatin 
4511b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
45126d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4513b2fc195eSAndrew Gallatin 
4514b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
45156d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
45166d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4517b2fc195eSAndrew Gallatin 	if (err != 0)
4518b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4519b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
45206d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4521b2fc195eSAndrew Gallatin 	if (err != 0)
4522b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4523b2fc195eSAndrew Gallatin 
4524a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4525a98d6cd7SAndrew Gallatin 	if (err != 0)
45261e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4527b2fc195eSAndrew Gallatin 
45288fe615baSAndrew Gallatin 	/* select & load the firmware */
45298fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4530b2fc195eSAndrew Gallatin 	if (err != 0)
45311e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
45325e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
45331e413cf9SAndrew Gallatin 
45341e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
45351e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
45361e413cf9SAndrew Gallatin 	if (err != 0)
45371e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
45381e413cf9SAndrew Gallatin 
4539adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4540b2fc195eSAndrew Gallatin 	if (err != 0)
45411e413cf9SAndrew Gallatin 		goto abort_with_slices;
4542b2fc195eSAndrew Gallatin 
4543a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4544a98d6cd7SAndrew Gallatin 	if (err != 0) {
4545a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
45461e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4547a98d6cd7SAndrew Gallatin 	}
4548a98d6cd7SAndrew Gallatin 
45491e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4550a98d6cd7SAndrew Gallatin 	if (err != 0) {
45511e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4552a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4553a98d6cd7SAndrew Gallatin 	}
45541e413cf9SAndrew Gallatin 
4555e5062938SAndrew Gallatin 	ifp->if_baudrate = IF_Gbps(10UL);
4556c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
4557eb6219e3SAndrew Gallatin 		IFCAP_VLAN_MTU;
4558eb6219e3SAndrew Gallatin #ifdef INET
4559eb6219e3SAndrew Gallatin 	ifp->if_capabilities |= IFCAP_LRO;
4560eb6219e3SAndrew Gallatin #endif
456137d89b0cSAndrew Gallatin 
456237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
456337d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
456437d89b0cSAndrew Gallatin #endif
4565c792928fSAndrew Gallatin 
4566053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4567053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4568053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4569053e637fSAndrew Gallatin 	else
4570053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4571adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4572053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4573aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4574b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4575f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4576f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
45775e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
45786d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4579b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4580b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
45816d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
45826d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4583c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4584c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4585c587e59fSAndrew Gallatin 		     mxge_media_status);
4586c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4587c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
4588b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4589b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
4590053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
4591c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
4592b2fc195eSAndrew Gallatin 
45936d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4594c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING
4595c6cb3e3fSAndrew Gallatin 	ifp->if_transmit = mxge_transmit;
4596c6cb3e3fSAndrew Gallatin 	ifp->if_qflush = mxge_qflush;
4597c6cb3e3fSAndrew Gallatin #endif
4598b2fc195eSAndrew Gallatin 	return 0;
4599b2fc195eSAndrew Gallatin 
4600a98d6cd7SAndrew Gallatin abort_with_rings:
4601a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
46021e413cf9SAndrew Gallatin abort_with_slices:
46031e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4604a98d6cd7SAndrew Gallatin abort_with_dmabench:
4605a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4606b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
46076d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4608b2fc195eSAndrew Gallatin abort_with_cmd_dma:
46096d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4610b2fc195eSAndrew Gallatin abort_with_mem_res:
4611b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4612b2fc195eSAndrew Gallatin abort_with_lock:
4613b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4614a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4615a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4616b2fc195eSAndrew Gallatin 	if_free(ifp);
4617b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4618b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4619b2fc195eSAndrew Gallatin 
4620b2fc195eSAndrew Gallatin abort_with_nothing:
4621b2fc195eSAndrew Gallatin 	return err;
4622b2fc195eSAndrew Gallatin }
4623b2fc195eSAndrew Gallatin 
4624b2fc195eSAndrew Gallatin static int
46256d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4626b2fc195eSAndrew Gallatin {
46276d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4628b2fc195eSAndrew Gallatin 
462937d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4630c792928fSAndrew Gallatin 		device_printf(sc->dev,
4631c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4632c792928fSAndrew Gallatin 		return EBUSY;
4633c792928fSAndrew Gallatin 	}
4634a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
4635b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
46366d87a65dSAndrew Gallatin 		mxge_close(sc);
4637a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4638b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4639e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4640dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4641091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
46421e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
46431e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4644a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
46451e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4646a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
46476d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
46486d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4649b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4650b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4651a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4652a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4653b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4654b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4655b2fc195eSAndrew Gallatin 	return 0;
4656b2fc195eSAndrew Gallatin }
4657b2fc195eSAndrew Gallatin 
4658b2fc195eSAndrew Gallatin static int
46596d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4660b2fc195eSAndrew Gallatin {
4661b2fc195eSAndrew Gallatin 	return 0;
4662b2fc195eSAndrew Gallatin }
4663b2fc195eSAndrew Gallatin 
4664b2fc195eSAndrew Gallatin /*
4665b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4666b2fc195eSAndrew Gallatin 
4667b2fc195eSAndrew Gallatin   Local Variables:
4668b2fc195eSAndrew Gallatin   c-file-style:"linux"
4669b2fc195eSAndrew Gallatin   tab-width:8
4670b2fc195eSAndrew Gallatin   End:
4671b2fc195eSAndrew Gallatin */
4672