xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 7ea3fd3bb5f4039c372fd72aeef004fe12454537)
16d87a65dSAndrew Gallatin /******************************************************************************
2*4d846d26SWarner Losh SPDX-License-Identifier: BSD-2-Clause
3b2fc195eSAndrew Gallatin 
4cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc.
5b2fc195eSAndrew Gallatin All rights reserved.
6b2fc195eSAndrew Gallatin 
7b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
8b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
9b2fc195eSAndrew Gallatin 
10b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
11b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
12b2fc195eSAndrew Gallatin 
13eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
14b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
15b2fc195eSAndrew Gallatin     this software without specific prior written permission.
16b2fc195eSAndrew Gallatin 
17b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
28b2fc195eSAndrew Gallatin 
29b2fc195eSAndrew Gallatin ***************************************************************************/
30b2fc195eSAndrew Gallatin 
31b2fc195eSAndrew Gallatin #include <sys/param.h>
32b2fc195eSAndrew Gallatin #include <sys/systm.h>
33b2fc195eSAndrew Gallatin #include <sys/linker.h>
34b2fc195eSAndrew Gallatin #include <sys/firmware.h>
35b2fc195eSAndrew Gallatin #include <sys/endian.h>
36b2fc195eSAndrew Gallatin #include <sys/sockio.h>
37b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
38b2fc195eSAndrew Gallatin #include <sys/malloc.h>
39b2fc195eSAndrew Gallatin #include <sys/kdb.h>
40b2fc195eSAndrew Gallatin #include <sys/kernel.h>
414e7f640dSJohn Baldwin #include <sys/lock.h>
42b2fc195eSAndrew Gallatin #include <sys/module.h>
43b2fc195eSAndrew Gallatin #include <sys/socket.h>
44b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
45b2fc195eSAndrew Gallatin #include <sys/sx.h>
4672c042dfSAndrew Gallatin #include <sys/taskqueue.h>
471dbf944aSXin LI #include <contrib/zlib/zlib.h>
481dbf944aSXin LI #include <dev/zlib/zcalloc.h>
49b2fc195eSAndrew Gallatin 
50b2fc195eSAndrew Gallatin #include <net/if.h>
5176039bc8SGleb Smirnoff #include <net/if_var.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 
62b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
63b2fc195eSAndrew Gallatin #include <netinet/in.h>
64b2fc195eSAndrew Gallatin #include <netinet/ip.h>
650a7a780eSAndrew Gallatin #include <netinet/ip6.h>
66aed8e389SAndrew Gallatin #include <netinet/tcp.h>
6726dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h>
680a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h>
69b2fc195eSAndrew Gallatin 
70b2fc195eSAndrew Gallatin #include <machine/bus.h>
71053e637fSAndrew Gallatin #include <machine/in_cksum.h>
72b2fc195eSAndrew Gallatin #include <machine/resource.h>
73b2fc195eSAndrew Gallatin #include <sys/bus.h>
74b2fc195eSAndrew Gallatin #include <sys/rman.h>
751e413cf9SAndrew Gallatin #include <sys/smp.h>
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
78b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
79e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */
80b2fc195eSAndrew Gallatin 
81b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
82b2fc195eSAndrew Gallatin #include <vm/pmap.h>
83b2fc195eSAndrew Gallatin 
84c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
85c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
86c2c14a69SAndrew Gallatin #endif
87c2c14a69SAndrew Gallatin 
886d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
896d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
901e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
916d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
92869c7348SAndrew Gallatin #include <sys/buf_ring.h>
93b2fc195eSAndrew Gallatin 
94eb6219e3SAndrew Gallatin #include "opt_inet.h"
950a7a780eSAndrew Gallatin #include "opt_inet6.h"
96eb6219e3SAndrew Gallatin 
97b2fc195eSAndrew Gallatin /* tunable params */
986d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
99d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
1006d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
1015e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
1026d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
1035e7d8541SAndrew Gallatin static int mxge_verbose = 0;
104dce01b9bSAndrew Gallatin static int mxge_ticks;
1051e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
1065769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
1071e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
108f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO;
10965c69066SAndrew Gallatin static int mxge_throttle = 0;
1106d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1116d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1121e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1131e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
114b2fc195eSAndrew Gallatin 
1156d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1166d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1176d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1186d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1196d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
120b2fc195eSAndrew Gallatin 
1216d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
122b2fc195eSAndrew Gallatin {
123b2fc195eSAndrew Gallatin   /* Device interface */
1246d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1256d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1266d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1276d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
12861bfd867SSofian Brabez 
12961bfd867SSofian Brabez   DEVMETHOD_END
130b2fc195eSAndrew Gallatin };
131b2fc195eSAndrew Gallatin 
1326d87a65dSAndrew Gallatin static driver_t mxge_driver =
133b2fc195eSAndrew Gallatin {
1346d87a65dSAndrew Gallatin   "mxge",
1356d87a65dSAndrew Gallatin   mxge_methods,
1366d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
137b2fc195eSAndrew Gallatin };
138b2fc195eSAndrew Gallatin 
139b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
14099b29e34SJohn Baldwin DRIVER_MODULE(mxge, pci, mxge_driver, 0, 0);
1416d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
142f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
143b2fc195eSAndrew Gallatin 
1441e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1458fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
146a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down);
147276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
148276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1498fe615baSAndrew Gallatin 
150b2fc195eSAndrew Gallatin static int
mxge_probe(device_t dev)1516d87a65dSAndrew Gallatin mxge_probe(device_t dev)
152b2fc195eSAndrew Gallatin {
15301638550SAndrew Gallatin 	int rev;
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
mxge_enable_wc(mxge_softc_t * sc)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 /* callback to get our DMA address */
197b2fc195eSAndrew Gallatin static void
mxge_dmamap_callback(void * arg,bus_dma_segment_t * segs,int nsegs,int error)1986d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
199b2fc195eSAndrew Gallatin 			 int error)
200b2fc195eSAndrew Gallatin {
201b2fc195eSAndrew Gallatin 	if (error == 0) {
202b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
203b2fc195eSAndrew Gallatin 	}
204b2fc195eSAndrew Gallatin }
205b2fc195eSAndrew Gallatin 
206b2fc195eSAndrew Gallatin static int
mxge_dma_alloc(mxge_softc_t * sc,mxge_dma_t * dma,size_t bytes,bus_size_t alignment)2076d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
208b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
209b2fc195eSAndrew Gallatin {
210b2fc195eSAndrew Gallatin 	int err;
211b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2121e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2131e413cf9SAndrew Gallatin 
2141e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2151e413cf9SAndrew Gallatin 		boundary = 0;
2161e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2171e413cf9SAndrew Gallatin 	} else {
2181e413cf9SAndrew Gallatin 		boundary = 4096;
2191e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2201e413cf9SAndrew Gallatin 	}
221b2fc195eSAndrew Gallatin 
222b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
223b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
224b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2251e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
226b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
227b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
228b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
229b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
230b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2311e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
232b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
233b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
234b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
235b2fc195eSAndrew Gallatin 	if (err != 0) {
236b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
237b2fc195eSAndrew Gallatin 		return err;
238b2fc195eSAndrew Gallatin 	}
239b2fc195eSAndrew Gallatin 
240b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
241b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
242b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
243b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
244b2fc195eSAndrew Gallatin 	if (err != 0) {
245b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
246b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
247b2fc195eSAndrew Gallatin 	}
248b2fc195eSAndrew Gallatin 
249b2fc195eSAndrew Gallatin 	/* load the memory */
250b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2516d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
252b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
253b2fc195eSAndrew Gallatin 	if (err != 0) {
254b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
255b2fc195eSAndrew Gallatin 		goto abort_with_mem;
256b2fc195eSAndrew Gallatin 	}
257b2fc195eSAndrew Gallatin 	return 0;
258b2fc195eSAndrew Gallatin 
259b2fc195eSAndrew Gallatin abort_with_mem:
260b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
261b2fc195eSAndrew Gallatin abort_with_dmat:
262b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
263b2fc195eSAndrew Gallatin 	return err;
264b2fc195eSAndrew Gallatin }
265b2fc195eSAndrew Gallatin 
266b2fc195eSAndrew Gallatin static void
mxge_dma_free(mxge_dma_t * dma)2676d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
268b2fc195eSAndrew Gallatin {
269b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
270b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
271b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
272b2fc195eSAndrew Gallatin }
273b2fc195eSAndrew Gallatin 
274b2fc195eSAndrew Gallatin /*
275b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
276b2fc195eSAndrew Gallatin  * SN=x\0
277b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
278b2fc195eSAndrew Gallatin  * PC=text\0
279b2fc195eSAndrew Gallatin  */
280b2fc195eSAndrew Gallatin 
281b2fc195eSAndrew Gallatin static int
mxge_parse_strings(mxge_softc_t * sc)2826d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
283b2fc195eSAndrew Gallatin {
284dedbe836SAndrew Gallatin 	char *ptr;
285a4b233ddSAndrew Gallatin 	int i, found_mac, found_sn2;
286dedbe836SAndrew Gallatin 	char *endptr;
287b2fc195eSAndrew Gallatin 
288b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
289b2fc195eSAndrew Gallatin 	found_mac = 0;
290a4b233ddSAndrew Gallatin 	found_sn2 = 0;
291dedbe836SAndrew Gallatin 	while (*ptr != '\0') {
292dedbe836SAndrew Gallatin 		if (strncmp(ptr, "MAC=", 4) == 0) {
293dedbe836SAndrew Gallatin 			ptr += 4;
294dedbe836SAndrew Gallatin 			for (i = 0;;) {
295dedbe836SAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
296dedbe836SAndrew Gallatin 				if (endptr - ptr != 2)
297b2fc195eSAndrew Gallatin 					goto abort;
298dedbe836SAndrew Gallatin 				ptr = endptr;
299dedbe836SAndrew Gallatin 				if (++i == 6)
300dedbe836SAndrew Gallatin 					break;
301dedbe836SAndrew Gallatin 				if (*ptr++ != ':')
302dedbe836SAndrew Gallatin 					goto abort;
303b2fc195eSAndrew Gallatin 			}
304dedbe836SAndrew Gallatin 			found_mac = 1;
305dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "PC=", 3) == 0) {
3065e7d8541SAndrew Gallatin 			ptr += 3;
307dedbe836SAndrew Gallatin 			strlcpy(sc->product_code_string, ptr,
308dedbe836SAndrew Gallatin 			    sizeof(sc->product_code_string));
309dedbe836SAndrew Gallatin 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
3105e7d8541SAndrew Gallatin 			ptr += 3;
311dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
312dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
313dedbe836SAndrew Gallatin 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
314a4b233ddSAndrew Gallatin 			/* SN2 takes precedence over SN */
315a4b233ddSAndrew Gallatin 			ptr += 4;
316a4b233ddSAndrew Gallatin 			found_sn2 = 1;
317dedbe836SAndrew Gallatin 			strlcpy(sc->serial_number_string, ptr,
318dedbe836SAndrew Gallatin 			    sizeof(sc->serial_number_string));
319b2fc195eSAndrew Gallatin 		}
320dedbe836SAndrew Gallatin 		while (*ptr++ != '\0') {}
321b2fc195eSAndrew Gallatin 	}
322b2fc195eSAndrew Gallatin 
323b2fc195eSAndrew Gallatin 	if (found_mac)
324b2fc195eSAndrew Gallatin 		return 0;
325b2fc195eSAndrew Gallatin 
326b2fc195eSAndrew Gallatin  abort:
327b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
328b2fc195eSAndrew Gallatin 
329b2fc195eSAndrew Gallatin 	return ENXIO;
330b2fc195eSAndrew Gallatin }
331b2fc195eSAndrew Gallatin 
3320d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3338fe615baSAndrew Gallatin static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)3348fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
335b2fc195eSAndrew Gallatin {
336b2fc195eSAndrew Gallatin 	uint32_t val;
3378fe615baSAndrew Gallatin 	unsigned long base, off;
338b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3398fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3408fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
341b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
342b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
343b2fc195eSAndrew Gallatin 
3448fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3458fe615baSAndrew Gallatin 		return;
3468fe615baSAndrew Gallatin 
3478fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3488fe615baSAndrew Gallatin 	if (pdev == NULL) {
3498fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3508fe615baSAndrew Gallatin 		return;
3518fe615baSAndrew Gallatin 	}
3528fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3538fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3548fe615baSAndrew Gallatin 
3558fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3568fe615baSAndrew Gallatin 		return;
3578fe615baSAndrew Gallatin 
3588fe615baSAndrew Gallatin 	base = 0;
3598fe615baSAndrew Gallatin 
3608fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3618fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3628fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3638fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3648fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3658fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3668fe615baSAndrew Gallatin 		if (mcp55 &&
3678fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3688fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3698fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3708fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3718fe615baSAndrew Gallatin 		}
3728fe615baSAndrew Gallatin 	}
3738fe615baSAndrew Gallatin 	if (!base)
3748fe615baSAndrew Gallatin 		return;
3758fe615baSAndrew Gallatin 
376b2fc195eSAndrew Gallatin 	/* XXXX
377b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
378b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
379b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
380b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
381b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
382b2fc195eSAndrew Gallatin 	*/
383b2fc195eSAndrew Gallatin #if 0
384b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
385b2fc195eSAndrew Gallatin 	   config space */
386b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
387b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
388b2fc195eSAndrew Gallatin 		val |= 0x40;
389b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3908fe615baSAndrew Gallatin 		return;
391b2fc195eSAndrew Gallatin 	}
392b2fc195eSAndrew Gallatin #endif
393b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
394b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
395b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
396b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
397b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
398b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
399b2fc195eSAndrew Gallatin 	 */
400b2fc195eSAndrew Gallatin 
401b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
402b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
403b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
404b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
405b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
406b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
407b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
408b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
409b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
410b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
411b2fc195eSAndrew Gallatin 
4128fe615baSAndrew Gallatin 	off =  base
413b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
414b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
415b2fc195eSAndrew Gallatin 						 + 8 * slot);
416b2fc195eSAndrew Gallatin 
417b2fc195eSAndrew Gallatin 	/* map it into the kernel */
418b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
419b2fc195eSAndrew Gallatin 
420b2fc195eSAndrew Gallatin 	if (va == NULL) {
421b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4228fe615baSAndrew Gallatin 		return;
423b2fc195eSAndrew Gallatin 	}
424b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
425b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
426b2fc195eSAndrew Gallatin 
427b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
428b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
429b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
430b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
431b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
432b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
4337ae99f80SJohn Baldwin 		pmap_unmapdev(va, PAGE_SIZE);
4348fe615baSAndrew Gallatin 		return;
435b2fc195eSAndrew Gallatin 	}
436b2fc195eSAndrew Gallatin 
437b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
438b2fc195eSAndrew Gallatin 	val = *ptr32;
439b2fc195eSAndrew Gallatin 
440b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
441b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
4427ae99f80SJohn Baldwin 		pmap_unmapdev(va, PAGE_SIZE);
4438fe615baSAndrew Gallatin 		return;
444b2fc195eSAndrew Gallatin 	}
445b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
4467ae99f80SJohn Baldwin 	pmap_unmapdev(va, PAGE_SIZE);
4475e7d8541SAndrew Gallatin 	if (mxge_verbose)
448b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4495e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4505e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
451b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4528fe615baSAndrew Gallatin 	return;
453b2fc195eSAndrew Gallatin }
454b2fc195eSAndrew Gallatin #else
4558fe615baSAndrew Gallatin static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)456f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
457b2fc195eSAndrew Gallatin {
458b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
459b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4608fe615baSAndrew Gallatin 	return;
461b2fc195eSAndrew Gallatin }
462b2fc195eSAndrew Gallatin #endif
4638fe615baSAndrew Gallatin 
4648fe615baSAndrew Gallatin static int
mxge_dma_test(mxge_softc_t * sc,int test_type)4658fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4668fe615baSAndrew Gallatin {
4678fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4688fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4698fe615baSAndrew Gallatin 	int status;
4708fe615baSAndrew Gallatin 	uint32_t len;
4718fe615baSAndrew Gallatin 	char *test = " ";
4728fe615baSAndrew Gallatin 
4738fe615baSAndrew Gallatin 	/* Run a small DMA test.
4748fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4758fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4768fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4778fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4788fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4798fe615baSAndrew Gallatin 	 * transfers took to complete.
4808fe615baSAndrew Gallatin 	 */
4818fe615baSAndrew Gallatin 
4821e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4838fe615baSAndrew Gallatin 
4848fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4858fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4868fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4878fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4888fe615baSAndrew Gallatin 	if (status != 0) {
4898fe615baSAndrew Gallatin 		test = "read";
4908fe615baSAndrew Gallatin 		goto abort;
4918fe615baSAndrew Gallatin 	}
4928fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4938fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4948fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4958fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4968fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4978fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4988fe615baSAndrew Gallatin 	if (status != 0) {
4998fe615baSAndrew Gallatin 		test = "write";
5008fe615baSAndrew Gallatin 		goto abort;
5018fe615baSAndrew Gallatin 	}
5028fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5038fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5048fe615baSAndrew Gallatin 
5058fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5068fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5078fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
5088fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
5098fe615baSAndrew Gallatin 	if (status != 0) {
5108fe615baSAndrew Gallatin 		test = "read/write";
5118fe615baSAndrew Gallatin 		goto abort;
5128fe615baSAndrew Gallatin 	}
5138fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5148fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5158fe615baSAndrew Gallatin 
5168fe615baSAndrew Gallatin abort:
5178fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5188fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5198fe615baSAndrew Gallatin 			      test, status);
5208fe615baSAndrew Gallatin 
5218fe615baSAndrew Gallatin 	return status;
5228fe615baSAndrew Gallatin }
5238fe615baSAndrew Gallatin 
524b2fc195eSAndrew Gallatin /*
525b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
526b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
527b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
528b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
529b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
530b2fc195eSAndrew Gallatin  *
531b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
532b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
533b2fc195eSAndrew Gallatin  *
534b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
535b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
536b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
537b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5381e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
539b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5401e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
541b2fc195eSAndrew Gallatin  */
542b2fc195eSAndrew Gallatin 
5438fe615baSAndrew Gallatin static int
mxge_firmware_probe(mxge_softc_t * sc)5448fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5458fe615baSAndrew Gallatin {
5468fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5478fe615baSAndrew Gallatin 	int reg, status;
5488fe615baSAndrew Gallatin 	uint16_t pectl;
5498fe615baSAndrew Gallatin 
5501e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5518fe615baSAndrew Gallatin 	/*
5528fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5538fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5548fe615baSAndrew Gallatin 	 */
5553b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
5568fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5578fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5588fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5598fe615baSAndrew Gallatin 				      pectl);
5601e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5618fe615baSAndrew Gallatin 		}
5628fe615baSAndrew Gallatin 	}
5638fe615baSAndrew Gallatin 
5648fe615baSAndrew Gallatin 	/*
5658fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5668fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5678fe615baSAndrew Gallatin 	 */
5688fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5691e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5708fe615baSAndrew Gallatin 	if (status != 0) {
5718fe615baSAndrew Gallatin 		return status;
5728fe615baSAndrew Gallatin 	}
5738fe615baSAndrew Gallatin 
5748fe615baSAndrew Gallatin 	/*
5758fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5768fe615baSAndrew Gallatin 	 */
5778fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5788fe615baSAndrew Gallatin 
5798fe615baSAndrew Gallatin 	/*
5808fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
581a4b233ddSAndrew Gallatin 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5828fe615baSAndrew Gallatin 	 */
583a4b233ddSAndrew Gallatin 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
584a4b233ddSAndrew Gallatin 		return 0;
5858fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5868fe615baSAndrew Gallatin 	if (status == 0)
5878fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5888fe615baSAndrew Gallatin 
5898fe615baSAndrew Gallatin 	if (status != E2BIG)
5908fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5918fe615baSAndrew Gallatin 	if (status == ENOSYS)
5928fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5938fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5948fe615baSAndrew Gallatin 	return status;
5958fe615baSAndrew Gallatin }
5968fe615baSAndrew Gallatin 
5978fe615baSAndrew Gallatin static int
mxge_select_firmware(mxge_softc_t * sc)5986d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
599b2fc195eSAndrew Gallatin {
6008fe615baSAndrew Gallatin 	int aligned = 0;
60165c69066SAndrew Gallatin 	int force_firmware = mxge_force_firmware;
602b2fc195eSAndrew Gallatin 
60365c69066SAndrew Gallatin 	if (sc->throttle)
60465c69066SAndrew Gallatin 		force_firmware = sc->throttle;
605d91b1b49SAndrew Gallatin 
60665c69066SAndrew Gallatin 	if (force_firmware != 0) {
60765c69066SAndrew Gallatin 		if (force_firmware == 1)
608d91b1b49SAndrew Gallatin 			aligned = 1;
609d91b1b49SAndrew Gallatin 		else
610d91b1b49SAndrew Gallatin 			aligned = 0;
611d91b1b49SAndrew Gallatin 		if (mxge_verbose)
612d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
613d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
614d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
615d91b1b49SAndrew Gallatin 		goto abort;
616d91b1b49SAndrew Gallatin 	}
617d91b1b49SAndrew Gallatin 
618d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
619d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
620d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
621d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
622d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
623d91b1b49SAndrew Gallatin 			      sc->link_width);
624d91b1b49SAndrew Gallatin 		aligned = 1;
625d91b1b49SAndrew Gallatin 		goto abort;
626d91b1b49SAndrew Gallatin 	}
627d91b1b49SAndrew Gallatin 
6288fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6298fe615baSAndrew Gallatin 		return 0;
630b2fc195eSAndrew Gallatin 
631b2fc195eSAndrew Gallatin abort:
632b2fc195eSAndrew Gallatin 	if (aligned) {
6336d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6341e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
635b2fc195eSAndrew Gallatin 	} else {
6366d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6371e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
638b2fc195eSAndrew Gallatin 	}
6391e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
640b2fc195eSAndrew Gallatin }
641b2fc195eSAndrew Gallatin 
6424da0d523SAndrew Gallatin static int
mxge_validate_firmware(mxge_softc_t * sc,const mcp_gen_header_t * hdr)6434da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6444da0d523SAndrew Gallatin {
645b824b7d8SAndrew Gallatin 
6464da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6474da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6484da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6494da0d523SAndrew Gallatin 		return EIO;
6504da0d523SAndrew Gallatin 	}
6514da0d523SAndrew Gallatin 
6524da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
653dedbe836SAndrew Gallatin 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6544da0d523SAndrew Gallatin 	if (mxge_verbose)
6554da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6564da0d523SAndrew Gallatin 
657b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
658b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6594da0d523SAndrew Gallatin 
660b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
661b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6624da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6634da0d523SAndrew Gallatin 			      sc->fw_version);
6644da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6654da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6664da0d523SAndrew Gallatin 		return EINVAL;
6674da0d523SAndrew Gallatin 	}
6684da0d523SAndrew Gallatin 	return 0;
6694da0d523SAndrew Gallatin 
6704da0d523SAndrew Gallatin }
671b2fc195eSAndrew Gallatin 
672b2fc195eSAndrew Gallatin static int
mxge_load_firmware_helper(mxge_softc_t * sc,uint32_t * limit)6736d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
674b2fc195eSAndrew Gallatin {
675f9ae0280SAndrew Gallatin 	z_stream zs;
676f9ae0280SAndrew Gallatin 	char *inflate_buffer;
67733d54970SLuigi Rizzo 	const struct firmware *fw;
678b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
679b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
680b2fc195eSAndrew Gallatin 	int status;
6814da0d523SAndrew Gallatin 	unsigned int i;
682f9ae0280SAndrew Gallatin 	size_t fw_len;
683b2fc195eSAndrew Gallatin 
684b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
685b2fc195eSAndrew Gallatin 	if (fw == NULL) {
686b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
687b2fc195eSAndrew Gallatin 			      sc->fw_name);
688b2fc195eSAndrew Gallatin 		return ENOENT;
689b2fc195eSAndrew Gallatin 	}
690b2fc195eSAndrew Gallatin 
691f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
692f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
6931dbf944aSXin LI 	zs.zalloc = zcalloc_nowait;
6941dbf944aSXin LI 	zs.zfree = zcfree;
695f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
696f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
697b2fc195eSAndrew Gallatin 		status = EIO;
698b2fc195eSAndrew Gallatin 		goto abort_with_fw;
699b2fc195eSAndrew Gallatin 	}
700f9ae0280SAndrew Gallatin 
701f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
702f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
703f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
704f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
705f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
706f9ae0280SAndrew Gallatin 		goto abort_with_zs;
707f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
708f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
709f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
710f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
711f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
712f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
713f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
714f9ae0280SAndrew Gallatin 		status = EIO;
715f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
716f9ae0280SAndrew Gallatin 	}
717f9ae0280SAndrew Gallatin 
718f9ae0280SAndrew Gallatin 	/* check id */
719f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
720f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
721f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
722f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
723f9ae0280SAndrew Gallatin 		status = EIO;
724f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
725f9ae0280SAndrew Gallatin 	}
726f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
727b2fc195eSAndrew Gallatin 
7284da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7294da0d523SAndrew Gallatin 	if (status != 0)
730f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
731b2fc195eSAndrew Gallatin 
732b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
733f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7344da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
735f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
736f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
73773c7c83fSAndrew Gallatin 		wmb();
738b08a56adSJohn Baldwin 		(void)*sc->sram;
73973c7c83fSAndrew Gallatin 		wmb();
7404da0d523SAndrew Gallatin 	}
741b2fc195eSAndrew Gallatin 
742f9ae0280SAndrew Gallatin 	*limit = fw_len;
743b2fc195eSAndrew Gallatin 	status = 0;
744f9ae0280SAndrew Gallatin abort_with_buffer:
745f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
746f9ae0280SAndrew Gallatin abort_with_zs:
747f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
748b2fc195eSAndrew Gallatin abort_with_fw:
749b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
750b2fc195eSAndrew Gallatin 	return status;
751b2fc195eSAndrew Gallatin }
752b2fc195eSAndrew Gallatin 
753b2fc195eSAndrew Gallatin /*
754b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
755b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
756b2fc195eSAndrew Gallatin  */
757b2fc195eSAndrew Gallatin 
758b2fc195eSAndrew Gallatin static void
mxge_dummy_rdma(mxge_softc_t * sc,int enable)7596d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
760b2fc195eSAndrew Gallatin {
761b2fc195eSAndrew Gallatin 	char buf_bytes[72];
762b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
763b2fc195eSAndrew Gallatin 	volatile char *submit;
764b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
765b2fc195eSAndrew Gallatin 	int i;
766b2fc195eSAndrew Gallatin 
767aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
768b2fc195eSAndrew Gallatin 
769b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
770b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
771b2fc195eSAndrew Gallatin 	*confirm = 0;
77273c7c83fSAndrew Gallatin 	wmb();
773b2fc195eSAndrew Gallatin 
774b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
775b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
776b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
777b2fc195eSAndrew Gallatin 	*/
778b2fc195eSAndrew Gallatin 
7796d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7806d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
781b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
782b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
783b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7846d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7856d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
786b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
787b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
788b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
789b2fc195eSAndrew Gallatin 
7900fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
791b2fc195eSAndrew Gallatin 
7926d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
79373c7c83fSAndrew Gallatin 	wmb();
794b2fc195eSAndrew Gallatin 	DELAY(1000);
79573c7c83fSAndrew Gallatin 	wmb();
796b2fc195eSAndrew Gallatin 	i = 0;
797b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
798b2fc195eSAndrew Gallatin 		DELAY(1000);
799b2fc195eSAndrew Gallatin 		i++;
800b2fc195eSAndrew Gallatin 	}
801b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
802b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
803b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
804b2fc195eSAndrew Gallatin 			      *confirm);
805b2fc195eSAndrew Gallatin 	}
806b2fc195eSAndrew Gallatin 	return;
807b2fc195eSAndrew Gallatin }
808b2fc195eSAndrew Gallatin 
809b2fc195eSAndrew Gallatin static int
mxge_send_cmd(mxge_softc_t * sc,uint32_t cmd,mxge_cmd_t * data)8106d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
811b2fc195eSAndrew Gallatin {
812b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
813b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
814b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8150fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
816b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
817e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
818b2fc195eSAndrew Gallatin 
819b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
820aa54c242SJohn Baldwin 	buf = (mcp_cmd_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
821b2fc195eSAndrew Gallatin 
822b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
823b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
824b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
825b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8266d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8276d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
828b2fc195eSAndrew Gallatin 
829b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
830b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
831a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
832b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
83373c7c83fSAndrew Gallatin 	wmb();
8346d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
835b2fc195eSAndrew Gallatin 
8365e7d8541SAndrew Gallatin 	/* wait up to 20ms */
837e0501fd0SAndrew Gallatin 	err = EAGAIN;
8385e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
839b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
840b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
84173c7c83fSAndrew Gallatin 		wmb();
842e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
843e0501fd0SAndrew Gallatin 		case 0:
844b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
845e0501fd0SAndrew Gallatin 			err = 0;
846e0501fd0SAndrew Gallatin 			break;
847e0501fd0SAndrew Gallatin 		case 0xffffffff:
848e0501fd0SAndrew Gallatin 			DELAY(1000);
849e0501fd0SAndrew Gallatin 			break;
850e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
851e0501fd0SAndrew Gallatin 			err = ENOSYS;
852e0501fd0SAndrew Gallatin 			break;
853e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
854e0501fd0SAndrew Gallatin 			err = E2BIG;
855e0501fd0SAndrew Gallatin 			break;
856c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
857c587e59fSAndrew Gallatin 			err = EBUSY;
858c587e59fSAndrew Gallatin 			break;
859c406ad2eSAndrew Gallatin 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
860c406ad2eSAndrew Gallatin 			err = ENXIO;
861c406ad2eSAndrew Gallatin 			break;
862e0501fd0SAndrew Gallatin 		default:
863b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8646d87a65dSAndrew Gallatin 				      "mxge: command %d "
865b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
866b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
867e0501fd0SAndrew Gallatin 			err = ENXIO;
868e0501fd0SAndrew Gallatin 			break;
869b2fc195eSAndrew Gallatin 		}
870e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
871e0501fd0SAndrew Gallatin 			break;
872b2fc195eSAndrew Gallatin 	}
873e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8746d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
875b2fc195eSAndrew Gallatin 			      "result = %d\n",
876b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
877e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
878e0501fd0SAndrew Gallatin 	return err;
879b2fc195eSAndrew Gallatin }
880b2fc195eSAndrew Gallatin 
8814da0d523SAndrew Gallatin static int
mxge_adopt_running_firmware(mxge_softc_t * sc)8824da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8834da0d523SAndrew Gallatin {
8844da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8854da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8864da0d523SAndrew Gallatin 	size_t hdr_offset;
8874da0d523SAndrew Gallatin 	int status;
8884da0d523SAndrew Gallatin 
8894da0d523SAndrew Gallatin 	/* find running firmware header */
8904da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8914da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8924da0d523SAndrew Gallatin 
8934da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8944da0d523SAndrew Gallatin 		device_printf(sc->dev,
8954da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8964da0d523SAndrew Gallatin 			      (int)hdr_offset);
8974da0d523SAndrew Gallatin 		return EIO;
8984da0d523SAndrew Gallatin 	}
8994da0d523SAndrew Gallatin 
9004da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9014da0d523SAndrew Gallatin 	 * validate firmware */
9024da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9034da0d523SAndrew Gallatin 	if (hdr == NULL) {
9044da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9054da0d523SAndrew Gallatin 		return ENOMEM;
9064da0d523SAndrew Gallatin 	}
9074da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9084da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9094da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9104da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9114da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
912b824b7d8SAndrew Gallatin 
913b824b7d8SAndrew Gallatin 	/*
914b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
915b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
916b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
917b824b7d8SAndrew Gallatin 	 */
918b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
919b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
920b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
921b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
922b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
923b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
924b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
925b824b7d8SAndrew Gallatin 	}
926b824b7d8SAndrew Gallatin 
9274da0d523SAndrew Gallatin 	return status;
9284da0d523SAndrew Gallatin }
9294da0d523SAndrew Gallatin 
930b2fc195eSAndrew Gallatin static int
mxge_load_firmware(mxge_softc_t * sc,int adopt)9311e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
932b2fc195eSAndrew Gallatin {
933b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
934b2fc195eSAndrew Gallatin 	volatile char *submit;
935b2fc195eSAndrew Gallatin 	char buf_bytes[72];
936b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
937b2fc195eSAndrew Gallatin 	int status, i;
938b2fc195eSAndrew Gallatin 
939aa54c242SJohn Baldwin 	buf = (uint32_t *)((uintptr_t)(buf_bytes + 7) & ~7UL);
940b2fc195eSAndrew Gallatin 
941b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9426d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
943b2fc195eSAndrew Gallatin 	if (status) {
9441e413cf9SAndrew Gallatin 		if (!adopt)
9451e413cf9SAndrew Gallatin 			return status;
9464da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9474da0d523SAndrew Gallatin 		   it is new enough */
9484da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9494da0d523SAndrew Gallatin 		if (status) {
9504da0d523SAndrew Gallatin 			device_printf(sc->dev,
9514da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
952b2fc195eSAndrew Gallatin 			return status;
953b2fc195eSAndrew Gallatin 		}
9544da0d523SAndrew Gallatin 		device_printf(sc->dev,
9554da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9561e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9574da0d523SAndrew Gallatin 			device_printf(sc->dev,
9584da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9594da0d523SAndrew Gallatin 				 ".  For optimal\n");
9604da0d523SAndrew Gallatin 			device_printf(sc->dev,
9614da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9624da0d523SAndrew Gallatin 				 "firmware\n");
9634da0d523SAndrew Gallatin 		}
964d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9651e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
966d91b1b49SAndrew Gallatin 		return 0;
9674da0d523SAndrew Gallatin 	}
968b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
969b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
970b2fc195eSAndrew Gallatin 	*confirm = 0;
97173c7c83fSAndrew Gallatin 	wmb();
972b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
973b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
974b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
975b2fc195eSAndrew Gallatin 	*/
976b2fc195eSAndrew Gallatin 
9776d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9786d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
979b2fc195eSAndrew Gallatin 
980b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
981b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
982b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
983b2fc195eSAndrew Gallatin 
984b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
985b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
986b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
987b2fc195eSAndrew Gallatin 	*/
988b2fc195eSAndrew Gallatin 					/* where the code starts*/
9896d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
990b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
991b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
992b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
993b2fc195eSAndrew Gallatin 
9940fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9956d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
99673c7c83fSAndrew Gallatin 	wmb();
997b2fc195eSAndrew Gallatin 	DELAY(1000);
99873c7c83fSAndrew Gallatin 	wmb();
999b2fc195eSAndrew Gallatin 	i = 0;
1000b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1001b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1002b2fc195eSAndrew Gallatin 		i++;
1003b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1004b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1005b2fc195eSAndrew Gallatin 	}
1006b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1007b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1008b2fc195eSAndrew Gallatin 			confirm, *confirm);
1009b2fc195eSAndrew Gallatin 
1010b2fc195eSAndrew Gallatin 		return ENXIO;
1011b2fc195eSAndrew Gallatin 	}
1012b2fc195eSAndrew Gallatin 	return 0;
1013b2fc195eSAndrew Gallatin }
1014b2fc195eSAndrew Gallatin 
1015b2fc195eSAndrew Gallatin static int
mxge_update_mac_address(mxge_softc_t * sc)10166d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1017b2fc195eSAndrew Gallatin {
10186d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1019b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1020b2fc195eSAndrew Gallatin 	int status;
1021b2fc195eSAndrew Gallatin 
1022b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1023b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1024b2fc195eSAndrew Gallatin 
1025b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1026b2fc195eSAndrew Gallatin 
10275e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1028b2fc195eSAndrew Gallatin 	return status;
1029b2fc195eSAndrew Gallatin }
1030b2fc195eSAndrew Gallatin 
1031b2fc195eSAndrew Gallatin static int
mxge_change_pause(mxge_softc_t * sc,int pause)10326d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1033b2fc195eSAndrew Gallatin {
10346d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1035b2fc195eSAndrew Gallatin 	int status;
1036b2fc195eSAndrew Gallatin 
1037b2fc195eSAndrew Gallatin 	if (pause)
10385e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1039b2fc195eSAndrew Gallatin 				       &cmd);
1040b2fc195eSAndrew Gallatin 	else
10415e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1042b2fc195eSAndrew Gallatin 				       &cmd);
1043b2fc195eSAndrew Gallatin 
1044b2fc195eSAndrew Gallatin 	if (status) {
1045b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1046b2fc195eSAndrew Gallatin 		return ENXIO;
1047b2fc195eSAndrew Gallatin 	}
1048b2fc195eSAndrew Gallatin 	sc->pause = pause;
1049b2fc195eSAndrew Gallatin 	return 0;
1050b2fc195eSAndrew Gallatin }
1051b2fc195eSAndrew Gallatin 
1052b2fc195eSAndrew Gallatin static void
mxge_change_promisc(mxge_softc_t * sc,int promisc)10536d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1054b2fc195eSAndrew Gallatin {
10556d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1056b2fc195eSAndrew Gallatin 	int status;
1057b2fc195eSAndrew Gallatin 
10581e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10591e413cf9SAndrew Gallatin 		promisc = 1;
10601e413cf9SAndrew Gallatin 
1061b2fc195eSAndrew Gallatin 	if (promisc)
10625e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1063b2fc195eSAndrew Gallatin 				       &cmd);
1064b2fc195eSAndrew Gallatin 	else
10655e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1066b2fc195eSAndrew Gallatin 				       &cmd);
1067b2fc195eSAndrew Gallatin 
1068b2fc195eSAndrew Gallatin 	if (status) {
1069b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1070b2fc195eSAndrew Gallatin 	}
1071b2fc195eSAndrew Gallatin }
1072b2fc195eSAndrew Gallatin 
10732a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx {
10742a0f8518SGleb Smirnoff 	mxge_softc_t *sc;
10752a0f8518SGleb Smirnoff 	int error;
10762a0f8518SGleb Smirnoff };
10772a0f8518SGleb Smirnoff 
10782a0f8518SGleb Smirnoff static u_int
mxge_add_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)10792a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
10802a0f8518SGleb Smirnoff {
10812a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx *ctx = arg;
10822a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
10832a0f8518SGleb Smirnoff 
10842a0f8518SGleb Smirnoff 	if (ctx->error != 0)
10852a0f8518SGleb Smirnoff 		return (0);
10862a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl), &cmd.data0, 4);
10872a0f8518SGleb Smirnoff 	bcopy(LLADDR(sdl) + 4, &cmd.data1, 2);
10882a0f8518SGleb Smirnoff 	cmd.data0 = htonl(cmd.data0);
10892a0f8518SGleb Smirnoff 	cmd.data1 = htonl(cmd.data1);
10902a0f8518SGleb Smirnoff 
10912a0f8518SGleb Smirnoff 	ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10922a0f8518SGleb Smirnoff 
10932a0f8518SGleb Smirnoff 	return (1);
10942a0f8518SGleb Smirnoff }
10952a0f8518SGleb Smirnoff 
10960fa7f681SAndrew Gallatin static void
mxge_set_multicast_list(mxge_softc_t * sc)10970fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10980fa7f681SAndrew Gallatin {
10992a0f8518SGleb Smirnoff 	struct mxge_add_maddr_ctx ctx;
110093037a67SJustin Hibbits 	if_t ifp = sc->ifp;
11012a0f8518SGleb Smirnoff 	mxge_cmd_t cmd;
11020fa7f681SAndrew Gallatin 	int err;
11030fa7f681SAndrew Gallatin 
11040fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
11050fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
11060fa7f681SAndrew Gallatin 		return;
11070fa7f681SAndrew Gallatin 
11080fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
11090fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11100fa7f681SAndrew Gallatin 	if (err != 0) {
11110fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11120fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11130fa7f681SAndrew Gallatin 		return;
11140fa7f681SAndrew Gallatin 	}
11150fa7f681SAndrew Gallatin 
1116b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1117b824b7d8SAndrew Gallatin 		return;
11180fa7f681SAndrew Gallatin 
111993037a67SJustin Hibbits 	if (if_getflags(ifp) & IFF_ALLMULTI)
11200fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11210fa7f681SAndrew Gallatin 		return;
11220fa7f681SAndrew Gallatin 
11230fa7f681SAndrew Gallatin 	/* Flush all the filters */
11240fa7f681SAndrew Gallatin 
11250fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11260fa7f681SAndrew Gallatin 	if (err != 0) {
11270fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11280fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11290fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11300fa7f681SAndrew Gallatin 		return;
11310fa7f681SAndrew Gallatin 	}
11320fa7f681SAndrew Gallatin 
11330fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11342a0f8518SGleb Smirnoff 	ctx.sc = sc;
11352a0f8518SGleb Smirnoff 	ctx.error = 0;
11362a0f8518SGleb Smirnoff 	if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx);
11372a0f8518SGleb Smirnoff 	if (ctx.error != 0) {
11382a0f8518SGleb Smirnoff 		device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
11392a0f8518SGleb Smirnoff 		    "error status:" "%d\t", ctx.error);
11400fa7f681SAndrew Gallatin 		/* abort, leaving multicast filtering off */
11410fa7f681SAndrew Gallatin 		return;
11420fa7f681SAndrew Gallatin 	}
11432a0f8518SGleb Smirnoff 
11440fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11450fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11460fa7f681SAndrew Gallatin 	if (err != 0) {
11470fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11480fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11490fa7f681SAndrew Gallatin 	}
11500fa7f681SAndrew Gallatin }
11510fa7f681SAndrew Gallatin 
1152b2fc195eSAndrew Gallatin static int
mxge_max_mtu(mxge_softc_t * sc)1153053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1154053e637fSAndrew Gallatin {
1155053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1156053e637fSAndrew Gallatin 	int status;
1157053e637fSAndrew Gallatin 
1158c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1159c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1160053e637fSAndrew Gallatin 
1161053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1162053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1163053e637fSAndrew Gallatin 	cmd.data0 = 0;
1164053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1165053e637fSAndrew Gallatin 			       &cmd);
1166053e637fSAndrew Gallatin 	if (status == 0)
1167c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1168053e637fSAndrew Gallatin 
1169053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1170053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1171053e637fSAndrew Gallatin }
1172053e637fSAndrew Gallatin 
1173053e637fSAndrew Gallatin static int
mxge_reset(mxge_softc_t * sc,int interrupts_setup)1174adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1175b2fc195eSAndrew Gallatin {
11761e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11771e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11781e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11796d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11801e413cf9SAndrew Gallatin 	int slice, status;
1181b2fc195eSAndrew Gallatin 
1182b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1183b2fc195eSAndrew Gallatin 	   is alive */
1184b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11855e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1186b2fc195eSAndrew Gallatin 	if (status != 0) {
1187b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1188b2fc195eSAndrew Gallatin 		return ENXIO;
1189b2fc195eSAndrew Gallatin 	}
1190b2fc195eSAndrew Gallatin 
1191091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1192091feecdSAndrew Gallatin 
11931e413cf9SAndrew Gallatin 	/* set the intrq size */
11941e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
11951e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11961e413cf9SAndrew Gallatin 
11971e413cf9SAndrew Gallatin 	/*
11981e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
11991e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12001e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12011e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12021e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12031e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12041e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12051e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12061e413cf9SAndrew Gallatin 	 */
12071e413cf9SAndrew Gallatin 
12081e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12091e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12101e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12111e413cf9SAndrew Gallatin 					   &cmd);
12121e413cf9SAndrew Gallatin 		if (status != 0) {
12131e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12141e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12151e413cf9SAndrew Gallatin 			return status;
12161e413cf9SAndrew Gallatin 		}
12171e413cf9SAndrew Gallatin 		/*
12181e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12191e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12201e413cf9SAndrew Gallatin 		 */
12211e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12221e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1223c6cb3e3fSAndrew Gallatin 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
12241e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12251e413cf9SAndrew Gallatin 					   &cmd);
12261e413cf9SAndrew Gallatin 		if (status != 0) {
12271e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12281e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12291e413cf9SAndrew Gallatin 			return status;
12301e413cf9SAndrew Gallatin 		}
12311e413cf9SAndrew Gallatin 	}
12321e413cf9SAndrew Gallatin 
1233adae7080SAndrew Gallatin 	if (interrupts_setup) {
1234b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12351e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12361e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12371e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12381e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12391e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12401e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12411e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12421e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12431e413cf9SAndrew Gallatin 						&cmd);
12441e413cf9SAndrew Gallatin 		}
1245adae7080SAndrew Gallatin 	}
1246b2fc195eSAndrew Gallatin 
12476d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12485e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12495e7d8541SAndrew Gallatin 
12505e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12515e7d8541SAndrew Gallatin 
12525e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12531e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12545e7d8541SAndrew Gallatin 
12555e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12566d87a65dSAndrew Gallatin 				&cmd);
12575e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1258b2fc195eSAndrew Gallatin 	if (status != 0) {
1259b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1260b2fc195eSAndrew Gallatin 		return status;
1261b2fc195eSAndrew Gallatin 	}
1262b2fc195eSAndrew Gallatin 
12635e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12645e7d8541SAndrew Gallatin 
12655e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12668fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12675e7d8541SAndrew Gallatin 
12681e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12691e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12701e413cf9SAndrew Gallatin 
12711e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1272b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12731e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12741e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12751e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12761e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12771e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
1278c6cb3e3fSAndrew Gallatin 		ss->tx.queue_active = 0;
1279c6cb3e3fSAndrew Gallatin 		ss->tx.activate = 0;
1280c6cb3e3fSAndrew Gallatin 		ss->tx.deactivate = 0;
12811e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12821e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12831e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12841e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12851e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
128626dd49c6SAndrew Gallatin 		ss->lc.lro_bad_csum = 0;
128726dd49c6SAndrew Gallatin 		ss->lc.lro_queued = 0;
128826dd49c6SAndrew Gallatin 		ss->lc.lro_flushed = 0;
12891e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
1290a393336bSAndrew Gallatin 			bzero(ss->fw_stats, sizeof *ss->fw_stats);
12911e413cf9SAndrew Gallatin 		}
12921e413cf9SAndrew Gallatin 	}
1293b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
12946d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
129593037a67SJustin Hibbits 	mxge_change_promisc(sc, if_getflags(sc->ifp) & IFF_PROMISC);
12966d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
12970fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
129865c69066SAndrew Gallatin 	if (sc->throttle) {
129965c69066SAndrew Gallatin 		cmd.data0 = sc->throttle;
130065c69066SAndrew Gallatin 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR,
130165c69066SAndrew Gallatin 				  &cmd)) {
130265c69066SAndrew Gallatin 			device_printf(sc->dev,
130365c69066SAndrew Gallatin 				      "can't enable throttle\n");
130465c69066SAndrew Gallatin 		}
130565c69066SAndrew Gallatin 	}
1306b2fc195eSAndrew Gallatin 	return status;
1307b2fc195eSAndrew Gallatin }
1308b2fc195eSAndrew Gallatin 
1309b2fc195eSAndrew Gallatin static int
mxge_change_throttle(SYSCTL_HANDLER_ARGS)131065c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS)
131165c69066SAndrew Gallatin {
131265c69066SAndrew Gallatin 	mxge_cmd_t cmd;
131365c69066SAndrew Gallatin 	mxge_softc_t *sc;
131465c69066SAndrew Gallatin 	int err;
131565c69066SAndrew Gallatin 	unsigned int throttle;
131665c69066SAndrew Gallatin 
131765c69066SAndrew Gallatin 	sc = arg1;
131865c69066SAndrew Gallatin 	throttle = sc->throttle;
131965c69066SAndrew Gallatin 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
132065c69066SAndrew Gallatin 	if (err != 0) {
132165c69066SAndrew Gallatin 		return err;
132265c69066SAndrew Gallatin 	}
132365c69066SAndrew Gallatin 
132465c69066SAndrew Gallatin 	if (throttle == sc->throttle)
132565c69066SAndrew Gallatin 		return 0;
132665c69066SAndrew Gallatin 
132765c69066SAndrew Gallatin 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
132865c69066SAndrew Gallatin 		return EINVAL;
132965c69066SAndrew Gallatin 
133065c69066SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
133165c69066SAndrew Gallatin 	cmd.data0 = throttle;
133265c69066SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
133365c69066SAndrew Gallatin 	if (err == 0)
133465c69066SAndrew Gallatin 		sc->throttle = throttle;
133565c69066SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
133665c69066SAndrew Gallatin 	return err;
133765c69066SAndrew Gallatin }
133865c69066SAndrew Gallatin 
133965c69066SAndrew Gallatin static int
mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)13406d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1341b2fc195eSAndrew Gallatin {
13426d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1343b2fc195eSAndrew Gallatin 	unsigned int intr_coal_delay;
1344b2fc195eSAndrew Gallatin 	int err;
1345b2fc195eSAndrew Gallatin 
1346b2fc195eSAndrew Gallatin 	sc = arg1;
1347b2fc195eSAndrew Gallatin 	intr_coal_delay = sc->intr_coal_delay;
1348b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1349b2fc195eSAndrew Gallatin 	if (err != 0) {
1350b2fc195eSAndrew Gallatin 		return err;
1351b2fc195eSAndrew Gallatin 	}
1352b2fc195eSAndrew Gallatin 	if (intr_coal_delay == sc->intr_coal_delay)
1353b2fc195eSAndrew Gallatin 		return 0;
1354b2fc195eSAndrew Gallatin 
1355b2fc195eSAndrew Gallatin 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1356b2fc195eSAndrew Gallatin 		return EINVAL;
1357b2fc195eSAndrew Gallatin 
1358a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13595e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1360b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13615e7d8541SAndrew Gallatin 
1362a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1363b2fc195eSAndrew Gallatin 	return err;
1364b2fc195eSAndrew Gallatin }
1365b2fc195eSAndrew Gallatin 
1366b2fc195eSAndrew Gallatin static int
mxge_change_flow_control(SYSCTL_HANDLER_ARGS)13676d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1368b2fc195eSAndrew Gallatin {
13696d87a65dSAndrew Gallatin 	mxge_softc_t *sc;
1370b2fc195eSAndrew Gallatin 	unsigned int enabled;
1371b2fc195eSAndrew Gallatin 	int err;
1372b2fc195eSAndrew Gallatin 
1373b2fc195eSAndrew Gallatin 	sc = arg1;
1374b2fc195eSAndrew Gallatin 	enabled = sc->pause;
1375b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
1376b2fc195eSAndrew Gallatin 	if (err != 0) {
1377b2fc195eSAndrew Gallatin 		return err;
1378b2fc195eSAndrew Gallatin 	}
1379b2fc195eSAndrew Gallatin 	if (enabled == sc->pause)
1380b2fc195eSAndrew Gallatin 		return 0;
1381b2fc195eSAndrew Gallatin 
1382a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13836d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1384a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1385b2fc195eSAndrew Gallatin 	return err;
1386b2fc195eSAndrew Gallatin }
1387b2fc195eSAndrew Gallatin 
1388b2fc195eSAndrew Gallatin static int
mxge_handle_be32(SYSCTL_HANDLER_ARGS)13896d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1390b2fc195eSAndrew Gallatin {
1391b2fc195eSAndrew Gallatin 	int err;
1392b2fc195eSAndrew Gallatin 
1393b2fc195eSAndrew Gallatin 	if (arg1 == NULL)
1394b2fc195eSAndrew Gallatin 		return EFAULT;
1395b2fc195eSAndrew Gallatin 	arg2 = be32toh(*(int *)arg1);
1396b2fc195eSAndrew Gallatin 	arg1 = NULL;
1397b2fc195eSAndrew Gallatin 	err = sysctl_handle_int(oidp, arg1, arg2, req);
1398b2fc195eSAndrew Gallatin 
1399b2fc195eSAndrew Gallatin 	return err;
1400b2fc195eSAndrew Gallatin }
1401b2fc195eSAndrew Gallatin 
1402b2fc195eSAndrew Gallatin static void
mxge_rem_sysctls(mxge_softc_t * sc)14031e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14041e413cf9SAndrew Gallatin {
14051e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14061e413cf9SAndrew Gallatin 	int slice;
14071e413cf9SAndrew Gallatin 
14081e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14091e413cf9SAndrew Gallatin 		return;
14101e413cf9SAndrew Gallatin 
14111e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14121e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14131e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14141e413cf9SAndrew Gallatin 			continue;
14151e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14161e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14171e413cf9SAndrew Gallatin 	}
14181e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14191e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14201e413cf9SAndrew Gallatin }
14211e413cf9SAndrew Gallatin 
14221e413cf9SAndrew Gallatin static void
mxge_add_sysctls(mxge_softc_t * sc)14236d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1424b2fc195eSAndrew Gallatin {
1425b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1426b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14275e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14281e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14291e413cf9SAndrew Gallatin 	int slice;
14301e413cf9SAndrew Gallatin 	char slice_num[8];
1431b2fc195eSAndrew Gallatin 
1432b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1433b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14341e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1435b2fc195eSAndrew Gallatin 
14365e7d8541SAndrew Gallatin 	/* random information */
14375e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14385e7d8541SAndrew Gallatin 		       "firmware_version",
1439f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->fw_version,
14405e7d8541SAndrew Gallatin 		       0, "firmware version");
14415e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14425e7d8541SAndrew Gallatin 		       "serial_number",
1443f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->serial_number_string,
14445e7d8541SAndrew Gallatin 		       0, "serial number");
14455e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14465e7d8541SAndrew Gallatin 		       "product_code",
1447f0188618SHans Petter Selasky 		       CTLFLAG_RD, sc->product_code_string,
14485e7d8541SAndrew Gallatin 		       0, "product_code");
14495e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1450d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1451d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1452d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1453d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14545e7d8541SAndrew Gallatin 		       "tx_boundary",
14551e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14565e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14575e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1458091feecdSAndrew Gallatin 		       "write_combine",
1459091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1460091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1461091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14625e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14635e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14645e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14655e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14665e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14675e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14685e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14695e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14705e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14715e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14725e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
1473a393336bSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1474a393336bSAndrew Gallatin 		       "watchdog_resets",
1475a393336bSAndrew Gallatin 		       CTLFLAG_RD, &sc->watchdog_resets,
1476a393336bSAndrew Gallatin 		       0, "Number of times NIC was reset");
14775e7d8541SAndrew Gallatin 
14785e7d8541SAndrew Gallatin 	/* performance related tunables */
1479b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14807029da5cSPawel Biernacki 	    "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
14817029da5cSPawel Biernacki 	    sc, 0, mxge_change_intr_coal, "I",
14827029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1483b2fc195eSAndrew Gallatin 
1484b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
14857029da5cSPawel Biernacki 	    "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14867029da5cSPawel Biernacki 	    mxge_change_throttle, "I", "transmit throttling");
148765c69066SAndrew Gallatin 
148865c69066SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1489b2fc195eSAndrew Gallatin 	    "flow_control_enabled",
14907029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14917029da5cSPawel Biernacki 	    mxge_change_flow_control, "I",
14927029da5cSPawel Biernacki 	    "interrupt coalescing delay in usecs");
1493b2fc195eSAndrew Gallatin 
1494b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14955e7d8541SAndrew Gallatin 		       "deassert_wait",
14965e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
14975e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1498b2fc195eSAndrew Gallatin 
1499b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1500b2fc195eSAndrew Gallatin 	   Need to swap it */
1501b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15027029da5cSPawel Biernacki 	    "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15037029da5cSPawel Biernacki 	    &fw->link_up, 0, mxge_handle_be32, "I", "link up");
1504b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15057029da5cSPawel Biernacki 	    "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15067029da5cSPawel Biernacki 	    &fw->rdma_tags_available, 0, mxge_handle_be32, "I",
15077029da5cSPawel Biernacki 	    "rdma_tags_available");
1508b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15097029da5cSPawel Biernacki 	    "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15107029da5cSPawel Biernacki 	    &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I",
15117029da5cSPawel Biernacki 	    "dropped_bad_crc32");
1512adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15137029da5cSPawel Biernacki 	    "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15147029da5cSPawel Biernacki 	    &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy");
1515b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1516b2fc195eSAndrew Gallatin 	    "dropped_link_error_or_filtered",
15177029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15187029da5cSPawel Biernacki 	    &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I",
15197029da5cSPawel Biernacki 	    "dropped_link_error_or_filtered");
1520b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1521adae7080SAndrew Gallatin 	    "dropped_link_overflow",
15227029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15237029da5cSPawel Biernacki 	    &fw->dropped_link_overflow, 0, mxge_handle_be32, "I",
15247029da5cSPawel Biernacki 	    "dropped_link_overflow");
1525adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15260fa7f681SAndrew Gallatin 	    "dropped_multicast_filtered",
15277029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15287029da5cSPawel Biernacki 	    &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I",
15297029da5cSPawel Biernacki 	    "dropped_multicast_filtered");
15300fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1531adae7080SAndrew Gallatin 	    "dropped_no_big_buffer",
15327029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15337029da5cSPawel Biernacki 	    &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I",
15347029da5cSPawel Biernacki 	    "dropped_no_big_buffer");
1535b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1536b2fc195eSAndrew Gallatin 	    "dropped_no_small_buffer",
15377029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15387029da5cSPawel Biernacki 	    &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I",
15397029da5cSPawel Biernacki 	    "dropped_no_small_buffer");
1540b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1541adae7080SAndrew Gallatin 	    "dropped_overrun",
15427029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15437029da5cSPawel Biernacki 	    &fw->dropped_overrun, 0, mxge_handle_be32, "I",
15447029da5cSPawel Biernacki 	    "dropped_overrun");
1545adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15467029da5cSPawel Biernacki 	    "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15477029da5cSPawel Biernacki 	    &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause");
1548adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15497029da5cSPawel Biernacki 	    "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15507029da5cSPawel Biernacki 	    &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt");
1551b2fc195eSAndrew Gallatin 
1552a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1553a0394e33SAndrew Gallatin 	    "dropped_unicast_filtered",
15547029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
15557029da5cSPawel Biernacki 	    &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I",
15567029da5cSPawel Biernacki 	    "dropped_unicast_filtered");
1557a0394e33SAndrew Gallatin 
15585e7d8541SAndrew Gallatin 	/* verbose printing? */
1559b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15605e7d8541SAndrew Gallatin 		       "verbose",
15615e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15625e7d8541SAndrew Gallatin 		       0, "verbose printing");
1563b2fc195eSAndrew Gallatin 
15641e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15651e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15661e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15671e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15687029da5cSPawel Biernacki 		    "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15691e413cf9SAndrew Gallatin 
15701e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
15711e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
15721e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
15731e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
15741e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
15751e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
15761e413cf9SAndrew Gallatin 		ss->sysctl_tree =
15771e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
15787029da5cSPawel Biernacki 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
15791e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1580053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15811e413cf9SAndrew Gallatin 			       "rx_small_cnt",
15821e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
15831e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
15841e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15851e413cf9SAndrew Gallatin 			       "rx_big_cnt",
15861e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
15871e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
1588e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
158926dd49c6SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed,
1590053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1591053e637fSAndrew Gallatin 
1592e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159326dd49c6SAndrew Gallatin 			       "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum,
159426dd49c6SAndrew Gallatin 			       0, "number of bad csums preventing LRO");
159526dd49c6SAndrew Gallatin 
1596e936121dSHans Petter Selasky 		SYSCTL_ADD_U64(ctx, children, OID_AUTO,
159726dd49c6SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued,
15981e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
15991e413cf9SAndrew Gallatin 			       "queues");
1600053e637fSAndrew Gallatin 
1601c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1602c6cb3e3fSAndrew Gallatin 			       "tx_req",
1603c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
1604c6cb3e3fSAndrew Gallatin 			       0, "tx_req");
16051e413cf9SAndrew Gallatin 
16061e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16071e413cf9SAndrew Gallatin 			       "tx_done",
16081e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16091e413cf9SAndrew Gallatin 			       0, "tx_done");
16101e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16111e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16121e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16131e413cf9SAndrew Gallatin 			       0, "tx_done");
16141e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16151e413cf9SAndrew Gallatin 			       "tx_stall",
16161e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16171e413cf9SAndrew Gallatin 			       0, "tx_stall");
16181e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16191e413cf9SAndrew Gallatin 			       "tx_wake",
16201e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16211e413cf9SAndrew Gallatin 			       0, "tx_wake");
16221e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16231e413cf9SAndrew Gallatin 			       "tx_defrag",
16241e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16251e413cf9SAndrew Gallatin 			       0, "tx_defrag");
1626c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1627c6cb3e3fSAndrew Gallatin 			       "tx_queue_active",
1628c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.queue_active,
1629c6cb3e3fSAndrew Gallatin 			       0, "tx_queue_active");
1630c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1631c6cb3e3fSAndrew Gallatin 			       "tx_activate",
1632c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.activate,
1633c6cb3e3fSAndrew Gallatin 			       0, "tx_activate");
1634c6cb3e3fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1635c6cb3e3fSAndrew Gallatin 			       "tx_deactivate",
1636c6cb3e3fSAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.deactivate,
1637c6cb3e3fSAndrew Gallatin 			       0, "tx_deactivate");
16381e413cf9SAndrew Gallatin 	}
1639b2fc195eSAndrew Gallatin }
1640b2fc195eSAndrew Gallatin 
1641b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1642b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1643b2fc195eSAndrew Gallatin 
1644b2fc195eSAndrew Gallatin static inline void
mxge_submit_req_backwards(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)16451e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1646b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1647b2fc195eSAndrew Gallatin {
1648b2fc195eSAndrew Gallatin 	int idx, starting_slot;
1649b2fc195eSAndrew Gallatin 	starting_slot = tx->req;
1650b2fc195eSAndrew Gallatin 	while (cnt > 1) {
1651b2fc195eSAndrew Gallatin 		cnt--;
1652b2fc195eSAndrew Gallatin 		idx = (starting_slot + cnt) & tx->mask;
16536d87a65dSAndrew Gallatin 		mxge_pio_copy(&tx->lanai[idx],
1654b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
165573c7c83fSAndrew Gallatin 		wmb();
1656b2fc195eSAndrew Gallatin 	}
1657b2fc195eSAndrew Gallatin }
1658b2fc195eSAndrew Gallatin 
1659b2fc195eSAndrew Gallatin /*
1660b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1661b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1662b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1663b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1664b2fc195eSAndrew Gallatin  */
1665b2fc195eSAndrew Gallatin 
1666b2fc195eSAndrew Gallatin static inline void
mxge_submit_req(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)16671e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1668b2fc195eSAndrew Gallatin 		  int cnt)
1669b2fc195eSAndrew Gallatin {
1670b2fc195eSAndrew Gallatin 	int idx, i;
1671b2fc195eSAndrew Gallatin 	uint32_t *src_ints;
1672b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1673b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *srcp;
1674b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16755e7d8541SAndrew Gallatin 	uint8_t last_flags;
1676b2fc195eSAndrew Gallatin 
1677b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1678b2fc195eSAndrew Gallatin 
16795e7d8541SAndrew Gallatin 	last_flags = src->flags;
16805e7d8541SAndrew Gallatin 	src->flags = 0;
168173c7c83fSAndrew Gallatin 	wmb();
1682b2fc195eSAndrew Gallatin 	dst = dstp = &tx->lanai[idx];
1683b2fc195eSAndrew Gallatin 	srcp = src;
1684b2fc195eSAndrew Gallatin 
1685b2fc195eSAndrew Gallatin 	if ((idx + cnt) < tx->mask) {
1686b2fc195eSAndrew Gallatin 		for (i = 0; i < (cnt - 1); i += 2) {
16876d87a65dSAndrew Gallatin 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
168873c7c83fSAndrew Gallatin 			wmb(); /* force write every 32 bytes */
1689b2fc195eSAndrew Gallatin 			srcp += 2;
1690b2fc195eSAndrew Gallatin 			dstp += 2;
1691b2fc195eSAndrew Gallatin 		}
1692b2fc195eSAndrew Gallatin 	} else {
1693b2fc195eSAndrew Gallatin 		/* submit all but the first request, and ensure
1694b2fc195eSAndrew Gallatin 		   that it is submitted below */
16956d87a65dSAndrew Gallatin 		mxge_submit_req_backwards(tx, src, cnt);
1696b2fc195eSAndrew Gallatin 		i = 0;
1697b2fc195eSAndrew Gallatin 	}
1698b2fc195eSAndrew Gallatin 	if (i < cnt) {
1699b2fc195eSAndrew Gallatin 		/* submit the first request */
17006d87a65dSAndrew Gallatin 		mxge_pio_copy(dstp, srcp, sizeof(*src));
170173c7c83fSAndrew Gallatin 		wmb(); /* barrier before setting valid flag */
1702b2fc195eSAndrew Gallatin 	}
1703b2fc195eSAndrew Gallatin 
1704b2fc195eSAndrew Gallatin 	/* re-write the last 32-bits with the valid flags */
17055e7d8541SAndrew Gallatin 	src->flags = last_flags;
1706b2fc195eSAndrew Gallatin 	src_ints = (uint32_t *)src;
1707b2fc195eSAndrew Gallatin 	src_ints+=3;
1708b2fc195eSAndrew Gallatin 	dst_ints = (volatile uint32_t *)dst;
1709b2fc195eSAndrew Gallatin 	dst_ints+=3;
1710b2fc195eSAndrew Gallatin 	*dst_ints =  *src_ints;
1711b2fc195eSAndrew Gallatin 	tx->req += cnt;
171273c7c83fSAndrew Gallatin 	wmb();
1713b2fc195eSAndrew Gallatin }
1714b2fc195eSAndrew Gallatin 
17150a7a780eSAndrew Gallatin static int
mxge_parse_tx(struct mxge_slice_state * ss,struct mbuf * m,struct mxge_pkt_info * pi)17160a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m,
17170a7a780eSAndrew Gallatin     struct mxge_pkt_info *pi)
17180a7a780eSAndrew Gallatin {
17190a7a780eSAndrew Gallatin 	struct ether_vlan_header *eh;
17200a7a780eSAndrew Gallatin 	uint16_t etype;
17210a7a780eSAndrew Gallatin 	int tso = m->m_pkthdr.csum_flags & (CSUM_TSO);
17220a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17230a7a780eSAndrew Gallatin 	int nxt;
17240a7a780eSAndrew Gallatin #endif
17250a7a780eSAndrew Gallatin 
17260a7a780eSAndrew Gallatin 	eh = mtod(m, struct ether_vlan_header *);
17270a7a780eSAndrew Gallatin 	if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
17280a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_proto);
17290a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
17300a7a780eSAndrew Gallatin 	} else {
17310a7a780eSAndrew Gallatin 		etype = ntohs(eh->evl_encap_proto);
17320a7a780eSAndrew Gallatin 		pi->ip_off = ETHER_HDR_LEN;
17330a7a780eSAndrew Gallatin 	}
17340a7a780eSAndrew Gallatin 
17350a7a780eSAndrew Gallatin 	switch (etype) {
17360a7a780eSAndrew Gallatin 	case ETHERTYPE_IP:
17370a7a780eSAndrew Gallatin 		/*
17380a7a780eSAndrew Gallatin 		 * ensure ip header is in first mbuf, copy it to a
17390a7a780eSAndrew Gallatin 		 * scratch buffer if not
17400a7a780eSAndrew Gallatin 		 */
17410a7a780eSAndrew Gallatin 		pi->ip = (struct ip *)(m->m_data + pi->ip_off);
17420a7a780eSAndrew Gallatin 		pi->ip6 = NULL;
17430a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) {
17440a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip),
17450a7a780eSAndrew Gallatin 			    ss->scratch);
17460a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17470a7a780eSAndrew Gallatin 		}
17480a7a780eSAndrew Gallatin 		pi->ip_hlen = pi->ip->ip_hl << 2;
17490a7a780eSAndrew Gallatin 		if (!tso)
17500a7a780eSAndrew Gallatin 			return 0;
17510a7a780eSAndrew Gallatin 
17520a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17530a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17540a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17550a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17560a7a780eSAndrew Gallatin 			pi->ip = (struct ip *)(ss->scratch + pi->ip_off);
17570a7a780eSAndrew Gallatin 		}
17580a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen);
17590a7a780eSAndrew Gallatin 		break;
17600a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6)
17610a7a780eSAndrew Gallatin 	case ETHERTYPE_IPV6:
17620a7a780eSAndrew Gallatin 		pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off);
17630a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) {
17640a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6),
17650a7a780eSAndrew Gallatin 			    ss->scratch);
17660a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17670a7a780eSAndrew Gallatin 		}
17680a7a780eSAndrew Gallatin 		nxt = 0;
17690a7a780eSAndrew Gallatin 		pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt);
17700a7a780eSAndrew Gallatin 		pi->ip_hlen -= pi->ip_off;
17710a7a780eSAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
17720a7a780eSAndrew Gallatin 			return EINVAL;
17730a7a780eSAndrew Gallatin 
17740a7a780eSAndrew Gallatin 		if (!tso)
17750a7a780eSAndrew Gallatin 			return 0;
17760a7a780eSAndrew Gallatin 
17770a7a780eSAndrew Gallatin 		if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen)
17780a7a780eSAndrew Gallatin 			return EINVAL;
17790a7a780eSAndrew Gallatin 
17800a7a780eSAndrew Gallatin 		if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen +
17810a7a780eSAndrew Gallatin 		    sizeof(struct tcphdr))) {
17820a7a780eSAndrew Gallatin 			m_copydata(m, 0, pi->ip_off + pi->ip_hlen +
17830a7a780eSAndrew Gallatin 			    sizeof(struct tcphdr), ss->scratch);
17840a7a780eSAndrew Gallatin 			pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off);
17850a7a780eSAndrew Gallatin 		}
17860a7a780eSAndrew Gallatin 		pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen);
17870a7a780eSAndrew Gallatin 		break;
17880a7a780eSAndrew Gallatin #endif
17890a7a780eSAndrew Gallatin 	default:
17900a7a780eSAndrew Gallatin 		return EINVAL;
17910a7a780eSAndrew Gallatin 	}
17920a7a780eSAndrew Gallatin 	return 0;
17930a7a780eSAndrew Gallatin }
17940a7a780eSAndrew Gallatin 
179537d89b0cSAndrew Gallatin #if IFCAP_TSO4
179637d89b0cSAndrew Gallatin 
1797b2fc195eSAndrew Gallatin static void
mxge_encap_tso(struct mxge_slice_state * ss,struct mbuf * m,int busdma_seg_cnt,struct mxge_pkt_info * pi)17981e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17990a7a780eSAndrew Gallatin 	       int busdma_seg_cnt, struct mxge_pkt_info *pi)
1800aed8e389SAndrew Gallatin {
18011e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1802aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1803aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1804aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1805aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1806aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
18070a7a780eSAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss, sum;
1808aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1809aed8e389SAndrew Gallatin 	static int once;
1810aed8e389SAndrew Gallatin 
1811aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1812aed8e389SAndrew Gallatin 
1813aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1814aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1815aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1816aed8e389SAndrew Gallatin 	 */
1817aed8e389SAndrew Gallatin 
18180a7a780eSAndrew Gallatin 	cksum_offset = pi->ip_off + pi->ip_hlen;
18190a7a780eSAndrew Gallatin 	cum_len = -(cksum_offset + (pi->tcp->th_off << 2));
1820aed8e389SAndrew Gallatin 
1821aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
18220a7a780eSAndrew Gallatin 	if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) {
18234ed8ca8fSAndrew Gallatin 		/*
18244ed8ca8fSAndrew Gallatin 		 * If packet has full TCP csum, replace it with pseudo hdr
18254ed8ca8fSAndrew Gallatin 		 * sum that the NIC expects, otherwise the NIC will emit
18264ed8ca8fSAndrew Gallatin 		 * packets with bad TCP checksums.
18274ed8ca8fSAndrew Gallatin 		 */
18284ed8ca8fSAndrew Gallatin 		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
18290a7a780eSAndrew Gallatin 		if (pi->ip6) {
18300a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6)
18310a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6;
18320a7a780eSAndrew Gallatin 			sum = in6_cksum_pseudo(pi->ip6,
18330a7a780eSAndrew Gallatin 			    m->m_pkthdr.len - cksum_offset,
18340a7a780eSAndrew Gallatin 			    IPPROTO_TCP, 0);
18350a7a780eSAndrew Gallatin #endif
18360a7a780eSAndrew Gallatin 		} else {
1837abc5b96bSAndrew Gallatin #ifdef INET
18380a7a780eSAndrew Gallatin 			m->m_pkthdr.csum_flags |= CSUM_TCP;
18390a7a780eSAndrew Gallatin 			sum = in_pseudo(pi->ip->ip_src.s_addr,
18400a7a780eSAndrew Gallatin 			    pi->ip->ip_dst.s_addr,
18410a7a780eSAndrew Gallatin 			    htons(IPPROTO_TCP + (m->m_pkthdr.len -
18420a7a780eSAndrew Gallatin 				    cksum_offset)));
1843abc5b96bSAndrew Gallatin #endif
18440a7a780eSAndrew Gallatin 		}
18450a7a780eSAndrew Gallatin 		m_copyback(m, offsetof(struct tcphdr, th_sum) +
18460a7a780eSAndrew Gallatin 		    cksum_offset, sizeof(sum), (caddr_t)&sum);
18474ed8ca8fSAndrew Gallatin 	}
1848aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1849aed8e389SAndrew Gallatin 
1850aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1851aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1852aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1853aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1854aed8e389SAndrew Gallatin 
18550a7a780eSAndrew Gallatin 	if (pi->ip6) {
18560a7a780eSAndrew Gallatin 		/*
18570a7a780eSAndrew Gallatin 		 * for IPv6 TSO, the "checksum offset" is re-purposed
18580a7a780eSAndrew Gallatin 		 * to store the TCP header len
18590a7a780eSAndrew Gallatin 		 */
18600a7a780eSAndrew Gallatin 		cksum_offset = (pi->tcp->th_off << 2);
18610a7a780eSAndrew Gallatin 	}
18620a7a780eSAndrew Gallatin 
18631e413cf9SAndrew Gallatin 	tx = &ss->tx;
1864aed8e389SAndrew Gallatin 	req = tx->req_list;
1865aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1866aed8e389SAndrew Gallatin 	cnt = 0;
1867aed8e389SAndrew Gallatin 	rdma_count = 0;
1868aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1869aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1870aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1871aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1872aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1873aed8e389SAndrew Gallatin 	 *
1874aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1875aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1876aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1877aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1878aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1879aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1880aed8e389SAndrew Gallatin 	 *
1881aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1882aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1883aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1884aed8e389SAndrew Gallatin 	 */
1885aed8e389SAndrew Gallatin 
1886aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1887aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1888aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1889aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1890e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1891aed8e389SAndrew Gallatin 
1892aed8e389SAndrew Gallatin 		while (len) {
1893aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1894e39a0a37SAndrew Gallatin 			seglen = len;
1895aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1896aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1897aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1898aed8e389SAndrew Gallatin 				/* payload */
1899aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1900aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1901aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1902aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1903aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1904aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1905aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1906aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1907aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1908aed8e389SAndrew Gallatin 				/* header ends */
1909aed8e389SAndrew Gallatin 				rdma_count = -1;
1910aed8e389SAndrew Gallatin 				cum_len_next = 0;
1911aed8e389SAndrew Gallatin 				seglen = -cum_len;
1912aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1913aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1914aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1915aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1916aed8e389SAndrew Gallatin 			    }
1917aed8e389SAndrew Gallatin 
1918aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1919aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1920aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1921aed8e389SAndrew Gallatin 			req->pad = 0;
1922aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1923aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1924aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1925aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1926aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1927aed8e389SAndrew Gallatin 			low += seglen;
1928aed8e389SAndrew Gallatin 			len -= seglen;
1929aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1930aed8e389SAndrew Gallatin 			flags = flags_next;
1931aed8e389SAndrew Gallatin 			req++;
1932aed8e389SAndrew Gallatin 			cnt++;
1933aed8e389SAndrew Gallatin 			rdma_count++;
19340a7a780eSAndrew Gallatin 			if (cksum_offset != 0 && !pi->ip6) {
1935aed8e389SAndrew Gallatin 				if (__predict_false(cksum_offset > seglen))
1936aed8e389SAndrew Gallatin 					cksum_offset -= seglen;
1937aed8e389SAndrew Gallatin 				else
1938aed8e389SAndrew Gallatin 					cksum_offset = 0;
19390a7a780eSAndrew Gallatin 			}
1940adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1941aed8e389SAndrew Gallatin 				goto drop;
1942aed8e389SAndrew Gallatin 		}
1943aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1944aed8e389SAndrew Gallatin 		seg++;
1945aed8e389SAndrew Gallatin 	}
1946aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1947aed8e389SAndrew Gallatin 
1948aed8e389SAndrew Gallatin 	do {
1949aed8e389SAndrew Gallatin 		req--;
1950aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1951aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1952aed8e389SAndrew Gallatin 
1953aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1954aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
19556147584bSElliott Mitchell 
1956c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
1957c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
1958c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
1959c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
1960c6cb3e3fSAndrew Gallatin 		tx->activate++;
1961c6cb3e3fSAndrew Gallatin 		wmb();
1962c6cb3e3fSAndrew Gallatin 	}
19636147584bSElliott Mitchell 
1964aed8e389SAndrew Gallatin 	return;
1965aed8e389SAndrew Gallatin 
1966aed8e389SAndrew Gallatin drop:
1967e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1968aed8e389SAndrew Gallatin 	m_freem(m);
1969c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
1970aed8e389SAndrew Gallatin 	if (!once) {
1971adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1972adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1973adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1974aed8e389SAndrew Gallatin 		once = 1;
1975aed8e389SAndrew Gallatin 	}
1976aed8e389SAndrew Gallatin 	return;
1977aed8e389SAndrew Gallatin 
1978aed8e389SAndrew Gallatin }
1979aed8e389SAndrew Gallatin 
198037d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
198137d89b0cSAndrew Gallatin 
198237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1983c792928fSAndrew Gallatin /*
1984c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1985c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1986c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1987c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1988c792928fSAndrew Gallatin  */
1989c792928fSAndrew Gallatin static struct mbuf *
mxge_vlan_tag_insert(struct mbuf * m)1990c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1991c792928fSAndrew Gallatin {
1992c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1993c792928fSAndrew Gallatin 
1994c6499eccSGleb Smirnoff 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
1995c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1996c792928fSAndrew Gallatin 		return NULL;
1997c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1998c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1999c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
2000c792928fSAndrew Gallatin 			return NULL;
2001c792928fSAndrew Gallatin 	}
2002c792928fSAndrew Gallatin 	/*
2003c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
2004c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
2005c792928fSAndrew Gallatin 	 */
2006c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2007c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
2008c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
2009c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
2010c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
2011c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
2012c792928fSAndrew Gallatin 	return m;
2013c792928fSAndrew Gallatin }
201437d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
2015c792928fSAndrew Gallatin 
2016aed8e389SAndrew Gallatin static void
mxge_encap(struct mxge_slice_state * ss,struct mbuf * m)20171e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
2018b2fc195eSAndrew Gallatin {
20190a7a780eSAndrew Gallatin 	struct mxge_pkt_info pi = {0,0,0,0};
20201e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2021b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
2022b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
2023b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
20241e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
20250a7a780eSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
2026aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
2027aed8e389SAndrew Gallatin 	uint8_t flags, cksum_offset;
2028b2fc195eSAndrew Gallatin 
20291e413cf9SAndrew Gallatin 	sc = ss->sc;
20301e413cf9SAndrew Gallatin 	tx = &ss->tx;
2031b2fc195eSAndrew Gallatin 
203237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2033c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
2034c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
2035c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
20360a7a780eSAndrew Gallatin 			goto drop_without_m;
2037c792928fSAndrew Gallatin 	}
203837d89b0cSAndrew Gallatin #endif
20390a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20400a7a780eSAndrew Gallatin 	    (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
20410a7a780eSAndrew Gallatin 		if (mxge_parse_tx(ss, m, &pi))
20420a7a780eSAndrew Gallatin 			goto drop;
20430a7a780eSAndrew Gallatin 	}
20440a7a780eSAndrew Gallatin 
2045b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
2046b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
2047b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
2048aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
2049b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
2050adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
2051b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
2052b2fc195eSAndrew Gallatin 		   to defrag */
2053b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
2054b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
2055b2fc195eSAndrew Gallatin 			goto drop;
2056b2fc195eSAndrew Gallatin 		}
20571e413cf9SAndrew Gallatin 		ss->tx.defrag++;
2058b2fc195eSAndrew Gallatin 		m = m_tmp;
2059b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
2060b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
2061aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
2062b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
2063b2fc195eSAndrew Gallatin 	}
2064adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
2065aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
2066aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
2067b2fc195eSAndrew Gallatin 		goto drop;
2068b2fc195eSAndrew Gallatin 	}
2069b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
2070b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
20715e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
2072b2fc195eSAndrew Gallatin 
207337d89b0cSAndrew Gallatin #if IFCAP_TSO4
2074aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
2075aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20760a7a780eSAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, &pi);
2077aed8e389SAndrew Gallatin 		return;
2078aed8e389SAndrew Gallatin 	}
207937d89b0cSAndrew Gallatin #endif
2080aed8e389SAndrew Gallatin 
2081b2fc195eSAndrew Gallatin 	req = tx->req_list;
2082b2fc195eSAndrew Gallatin 	cksum_offset = 0;
20835e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
20845e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
2085b2fc195eSAndrew Gallatin 
2086b2fc195eSAndrew Gallatin 	/* checksum offloading? */
20870a7a780eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags &
20880a7a780eSAndrew Gallatin 	    (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) {
2089aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2090aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
20910a7a780eSAndrew Gallatin 		cksum_offset = pi.ip_off + pi.ip_hlen;
2092b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20935e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2094b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20955e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2096aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2097aed8e389SAndrew Gallatin 	} else {
2098aed8e389SAndrew Gallatin 		odd_flag = 0;
2099b2fc195eSAndrew Gallatin 	}
21005e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
21015e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2102b2fc195eSAndrew Gallatin 
2103b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2104b2fc195eSAndrew Gallatin 	cum_len = 0;
2105aed8e389SAndrew Gallatin 	seg = tx->seg_list;
21065e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2107b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2108b2fc195eSAndrew Gallatin 		req->addr_low =
21096d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2110b2fc195eSAndrew Gallatin 		req->addr_high =
21116d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2112b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2113b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2114b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2115b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2116b2fc195eSAndrew Gallatin 		else
2117b2fc195eSAndrew Gallatin 			cksum_offset = 0;
21185e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21195e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21205e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2121aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2122b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2123b2fc195eSAndrew Gallatin 		seg++;
2124b2fc195eSAndrew Gallatin 		req++;
2125b2fc195eSAndrew Gallatin 		req->flags = 0;
2126b2fc195eSAndrew Gallatin 	}
2127b2fc195eSAndrew Gallatin 	req--;
2128b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2129b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2130b2fc195eSAndrew Gallatin 		req++;
2131b2fc195eSAndrew Gallatin 		req->addr_low =
21326d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2133b2fc195eSAndrew Gallatin 		req->addr_high =
21346d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2135b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
21365e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
21375e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21385e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
21395e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2140aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2141b2fc195eSAndrew Gallatin 		cnt++;
2142b2fc195eSAndrew Gallatin 	}
21435e7d8541SAndrew Gallatin 
21445e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
21455e7d8541SAndrew Gallatin #if 0
21465e7d8541SAndrew Gallatin 	/* print what the firmware will see */
21475e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
21485e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21495e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
21505e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
21515e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
21525e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
21535e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21545e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21555e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
21565e7d8541SAndrew Gallatin 	}
21575e7d8541SAndrew Gallatin 	printf("--------------\n");
21585e7d8541SAndrew Gallatin #endif
21595e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21606d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
21616147584bSElliott Mitchell 
2162c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
2163c6cb3e3fSAndrew Gallatin 		/* tell the NIC to start polling this slice */
2164c6cb3e3fSAndrew Gallatin 		*tx->send_go = 1;
2165c6cb3e3fSAndrew Gallatin 		tx->queue_active = 1;
2166c6cb3e3fSAndrew Gallatin 		tx->activate++;
2167c6cb3e3fSAndrew Gallatin 		wmb();
2168c6cb3e3fSAndrew Gallatin 	}
21696147584bSElliott Mitchell 
2170b2fc195eSAndrew Gallatin 	return;
2171b2fc195eSAndrew Gallatin 
2172b2fc195eSAndrew Gallatin drop:
2173b2fc195eSAndrew Gallatin 	m_freem(m);
21740a7a780eSAndrew Gallatin drop_without_m:
2175c6cb3e3fSAndrew Gallatin 	ss->oerrors++;
2176b2fc195eSAndrew Gallatin 	return;
2177b2fc195eSAndrew Gallatin }
2178b2fc195eSAndrew Gallatin 
2179c6cb3e3fSAndrew Gallatin static void
mxge_qflush(if_t ifp)218093037a67SJustin Hibbits mxge_qflush(if_t ifp)
2181c6cb3e3fSAndrew Gallatin {
218293037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
2183c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2184c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
2185c6cb3e3fSAndrew Gallatin 	int slice;
2186b2fc195eSAndrew Gallatin 
2187c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
2188c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[slice].tx;
2189c6cb3e3fSAndrew Gallatin 		mtx_lock(&tx->mtx);
2190c6cb3e3fSAndrew Gallatin 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
2191c6cb3e3fSAndrew Gallatin 			m_freem(m);
2192c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2193c6cb3e3fSAndrew Gallatin 	}
2194c6cb3e3fSAndrew Gallatin 	if_qflush(ifp);
2195c6cb3e3fSAndrew Gallatin }
21966d914a32SAndrew Gallatin 
2197c6cb3e3fSAndrew Gallatin static inline void
mxge_start_locked(struct mxge_slice_state * ss)2198c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2199c6cb3e3fSAndrew Gallatin {
2200c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
2201c6cb3e3fSAndrew Gallatin 	struct mbuf *m;
220293037a67SJustin Hibbits 	if_t ifp;
2203c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2204c6cb3e3fSAndrew Gallatin 
2205c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2206c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2207c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2208c6cb3e3fSAndrew Gallatin 
2209c6cb3e3fSAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2210c6cb3e3fSAndrew Gallatin 		m = drbr_dequeue(ifp, tx->br);
2211c6cb3e3fSAndrew Gallatin 		if (m == NULL) {
2212c6cb3e3fSAndrew Gallatin 			return;
2213c6cb3e3fSAndrew Gallatin 		}
2214c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2215c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2216c6cb3e3fSAndrew Gallatin 
2217c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2218c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2219c6cb3e3fSAndrew Gallatin 	}
2220c6cb3e3fSAndrew Gallatin 	/* ran out of transmit slots */
2221c6cb3e3fSAndrew Gallatin 	if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0)
2222c6cb3e3fSAndrew Gallatin 	    && (!drbr_empty(ifp, tx->br))) {
2223c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_OACTIVE;
2224c6cb3e3fSAndrew Gallatin 		tx->stall++;
2225c6cb3e3fSAndrew Gallatin 	}
2226c6cb3e3fSAndrew Gallatin }
2227c6cb3e3fSAndrew Gallatin 
2228c6cb3e3fSAndrew Gallatin static int
mxge_transmit_locked(struct mxge_slice_state * ss,struct mbuf * m)2229c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
2230c6cb3e3fSAndrew Gallatin {
2231c6cb3e3fSAndrew Gallatin 	mxge_softc_t *sc;
223293037a67SJustin Hibbits 	if_t ifp;
2233c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2234c6cb3e3fSAndrew Gallatin 	int err;
2235c6cb3e3fSAndrew Gallatin 
2236c6cb3e3fSAndrew Gallatin 	sc = ss->sc;
2237c6cb3e3fSAndrew Gallatin 	ifp = sc->ifp;
2238c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2239c6cb3e3fSAndrew Gallatin 
2240c6cb3e3fSAndrew Gallatin 	if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
2241c6cb3e3fSAndrew Gallatin 	    IFF_DRV_RUNNING) {
2242c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2243c6cb3e3fSAndrew Gallatin 		return (err);
2244c6cb3e3fSAndrew Gallatin 	}
2245c6cb3e3fSAndrew Gallatin 
2246193cbc4dSMax Laier 	if (!drbr_needs_enqueue(ifp, tx->br) &&
2247c6cb3e3fSAndrew Gallatin 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
2248c6cb3e3fSAndrew Gallatin 		/* let BPF see it */
2249c6cb3e3fSAndrew Gallatin 		BPF_MTAP(ifp, m);
2250c6cb3e3fSAndrew Gallatin 		/* give it to the nic */
2251c6cb3e3fSAndrew Gallatin 		mxge_encap(ss, m);
2252c6cb3e3fSAndrew Gallatin 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
2253c6cb3e3fSAndrew Gallatin 		return (err);
2254c6cb3e3fSAndrew Gallatin 	}
2255c6cb3e3fSAndrew Gallatin 	if (!drbr_empty(ifp, tx->br))
2256c6cb3e3fSAndrew Gallatin 		mxge_start_locked(ss);
2257c6cb3e3fSAndrew Gallatin 	return (0);
2258c6cb3e3fSAndrew Gallatin }
2259c6cb3e3fSAndrew Gallatin 
2260c6cb3e3fSAndrew Gallatin static int
mxge_transmit(if_t ifp,struct mbuf * m)226193037a67SJustin Hibbits mxge_transmit(if_t ifp, struct mbuf *m)
2262c6cb3e3fSAndrew Gallatin {
226393037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
2264c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
2265c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
2266c6cb3e3fSAndrew Gallatin 	int err = 0;
2267c6cb3e3fSAndrew Gallatin 	int slice;
2268c6cb3e3fSAndrew Gallatin 
2269c6cb3e3fSAndrew Gallatin 	slice = m->m_pkthdr.flowid;
2270c6cb3e3fSAndrew Gallatin 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
2271c6cb3e3fSAndrew Gallatin 
2272c6cb3e3fSAndrew Gallatin 	ss = &sc->ss[slice];
2273c6cb3e3fSAndrew Gallatin 	tx = &ss->tx;
2274c6cb3e3fSAndrew Gallatin 
2275c6cb3e3fSAndrew Gallatin 	if (mtx_trylock(&tx->mtx)) {
2276c6cb3e3fSAndrew Gallatin 		err = mxge_transmit_locked(ss, m);
2277c6cb3e3fSAndrew Gallatin 		mtx_unlock(&tx->mtx);
2278c6cb3e3fSAndrew Gallatin 	} else {
2279c6cb3e3fSAndrew Gallatin 		err = drbr_enqueue(ifp, tx->br, m);
2280c6cb3e3fSAndrew Gallatin 	}
2281c6cb3e3fSAndrew Gallatin 
2282c6cb3e3fSAndrew Gallatin 	return (err);
2283c6cb3e3fSAndrew Gallatin }
2284c6cb3e3fSAndrew Gallatin 
2285b2fc195eSAndrew Gallatin static void
mxge_start(if_t ifp)228693037a67SJustin Hibbits mxge_start(if_t ifp)
2287b2fc195eSAndrew Gallatin {
228893037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
22891e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2290b2fc195eSAndrew Gallatin 
22911e413cf9SAndrew Gallatin 	/* only use the first slice for now */
22921e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
22931e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
22941e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
22951e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2296b2fc195eSAndrew Gallatin }
2297b2fc195eSAndrew Gallatin 
22985e7d8541SAndrew Gallatin /*
22995e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23005e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
23015e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
23025e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
23035e7d8541SAndrew Gallatin  * in a burst
23045e7d8541SAndrew Gallatin  */
23055e7d8541SAndrew Gallatin static inline void
mxge_submit_8rx(volatile mcp_kreq_ether_recv_t * dst,mcp_kreq_ether_recv_t * src)23065e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23075e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
23085e7d8541SAndrew Gallatin {
23095e7d8541SAndrew Gallatin 	uint32_t low;
23105e7d8541SAndrew Gallatin 
23115e7d8541SAndrew Gallatin 	low = src->addr_low;
23125e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2313a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
231473c7c83fSAndrew Gallatin 	wmb();
2315a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
231673c7c83fSAndrew Gallatin 	wmb();
231740385a5fSAndrew Gallatin 	src->addr_low = low;
23185e7d8541SAndrew Gallatin 	dst->addr_low = low;
231973c7c83fSAndrew Gallatin 	wmb();
23205e7d8541SAndrew Gallatin }
23215e7d8541SAndrew Gallatin 
2322b2fc195eSAndrew Gallatin static int
mxge_get_buf_small(struct mxge_slice_state * ss,bus_dmamap_t map,int idx)23231e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2324b2fc195eSAndrew Gallatin {
2325b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2326b2fc195eSAndrew Gallatin 	struct mbuf *m;
23271e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2328b2fc195eSAndrew Gallatin 	int cnt, err;
2329b2fc195eSAndrew Gallatin 
2330c6499eccSGleb Smirnoff 	m = m_gethdr(M_NOWAIT, MT_DATA);
2331b2fc195eSAndrew Gallatin 	if (m == NULL) {
2332b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2333b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2334b2fc195eSAndrew Gallatin 		goto done;
2335b2fc195eSAndrew Gallatin 	}
2336b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2337b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2338b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2339b2fc195eSAndrew Gallatin 	if (err != 0) {
2340b2fc195eSAndrew Gallatin 		m_free(m);
2341b2fc195eSAndrew Gallatin 		goto done;
2342b2fc195eSAndrew Gallatin 	}
2343b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2344b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
23456d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2346b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
23476d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2348b2fc195eSAndrew Gallatin 
2349b2fc195eSAndrew Gallatin done:
2350adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2351adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2352b2fc195eSAndrew Gallatin 	return err;
2353b2fc195eSAndrew Gallatin }
2354b2fc195eSAndrew Gallatin 
2355b2fc195eSAndrew Gallatin static int
mxge_get_buf_big(struct mxge_slice_state * ss,bus_dmamap_t map,int idx)23561e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2357b2fc195eSAndrew Gallatin {
2358053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2359b2fc195eSAndrew Gallatin 	struct mbuf *m;
23601e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2361053e637fSAndrew Gallatin 	int cnt, err, i;
2362b2fc195eSAndrew Gallatin 
2363c6499eccSGleb Smirnoff 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2364b2fc195eSAndrew Gallatin 	if (m == NULL) {
2365b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2366b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2367b2fc195eSAndrew Gallatin 		goto done;
2368b2fc195eSAndrew Gallatin 	}
23694d9a5852SAndrew Gallatin 	m->m_len = rx->mlen;
2370b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2371053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2372b2fc195eSAndrew Gallatin 	if (err != 0) {
2373b2fc195eSAndrew Gallatin 		m_free(m);
2374b2fc195eSAndrew Gallatin 		goto done;
2375b2fc195eSAndrew Gallatin 	}
2376b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2377b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2378b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2379b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2380b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2381053e637fSAndrew Gallatin 
2382b2fc195eSAndrew Gallatin done:
2383053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2384b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
23855e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
23865e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2387b2fc195eSAndrew Gallatin 		}
2388053e637fSAndrew Gallatin 		idx++;
2389053e637fSAndrew Gallatin 	}
2390b2fc195eSAndrew Gallatin 	return err;
2391b2fc195eSAndrew Gallatin }
2392b2fc195eSAndrew Gallatin 
239326dd49c6SAndrew Gallatin #ifdef INET6
239426dd49c6SAndrew Gallatin 
239526dd49c6SAndrew Gallatin static uint16_t
mxge_csum_generic(uint16_t * raw,int len)239626dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len)
239726dd49c6SAndrew Gallatin {
239826dd49c6SAndrew Gallatin 	uint32_t csum;
239926dd49c6SAndrew Gallatin 
240026dd49c6SAndrew Gallatin 	csum = 0;
240126dd49c6SAndrew Gallatin 	while (len > 0) {
240226dd49c6SAndrew Gallatin 		csum += *raw;
240326dd49c6SAndrew Gallatin 		raw++;
240426dd49c6SAndrew Gallatin 		len -= 2;
240526dd49c6SAndrew Gallatin 	}
240626dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
240726dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xffff);
240826dd49c6SAndrew Gallatin 	return (uint16_t)csum;
240926dd49c6SAndrew Gallatin }
241026dd49c6SAndrew Gallatin 
241126dd49c6SAndrew Gallatin static inline uint16_t
mxge_rx_csum6(void * p,struct mbuf * m,uint32_t csum)241226dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum)
241326dd49c6SAndrew Gallatin {
241426dd49c6SAndrew Gallatin 	uint32_t partial;
241526dd49c6SAndrew Gallatin 	int nxt, cksum_offset;
241626dd49c6SAndrew Gallatin 	struct ip6_hdr *ip6 = p;
241726dd49c6SAndrew Gallatin 	uint16_t c;
241826dd49c6SAndrew Gallatin 
241926dd49c6SAndrew Gallatin 	nxt = ip6->ip6_nxt;
242026dd49c6SAndrew Gallatin 	cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN;
242126dd49c6SAndrew Gallatin 	if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) {
242226dd49c6SAndrew Gallatin 		cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN,
242326dd49c6SAndrew Gallatin 					   IPPROTO_IPV6, &nxt);
242426dd49c6SAndrew Gallatin 		if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP)
242526dd49c6SAndrew Gallatin 			return (1);
242626dd49c6SAndrew Gallatin 	}
242726dd49c6SAndrew Gallatin 
242826dd49c6SAndrew Gallatin 	/*
242926dd49c6SAndrew Gallatin 	 * IPv6 headers do not contain a checksum, and hence
243026dd49c6SAndrew Gallatin 	 * do not checksum to zero, so they don't "fall out"
243126dd49c6SAndrew Gallatin 	 * of the partial checksum calculation like IPv4
243226dd49c6SAndrew Gallatin 	 * headers do.  We need to fix the partial checksum by
243326dd49c6SAndrew Gallatin 	 * subtracting the checksum of the IPv6 header.
243426dd49c6SAndrew Gallatin 	 */
243526dd49c6SAndrew Gallatin 
243626dd49c6SAndrew Gallatin 	partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset -
243726dd49c6SAndrew Gallatin 				    ETHER_HDR_LEN);
243826dd49c6SAndrew Gallatin 	csum += ~partial;
243926dd49c6SAndrew Gallatin 	csum +=	 (csum < ~partial);
244026dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
244126dd49c6SAndrew Gallatin 	csum = (csum >> 16) + (csum & 0xFFFF);
244226dd49c6SAndrew Gallatin 	c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt,
244326dd49c6SAndrew Gallatin 			     csum);
244426dd49c6SAndrew Gallatin 	c ^= 0xffff;
244526dd49c6SAndrew Gallatin 	return (c);
244626dd49c6SAndrew Gallatin }
244726dd49c6SAndrew Gallatin #endif /* INET6 */
24489b03b0f3SAndrew Gallatin /*
24499b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
24509b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
24519b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
24529b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2453053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2454053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
24559b03b0f3SAndrew Gallatin  */
24569b03b0f3SAndrew Gallatin 
2457053e637fSAndrew Gallatin static inline uint16_t
mxge_rx_csum(struct mbuf * m,int csum)2458053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2459053e637fSAndrew Gallatin {
2460053e637fSAndrew Gallatin 	struct ether_header *eh;
246126dd49c6SAndrew Gallatin #ifdef INET
2462053e637fSAndrew Gallatin 	struct ip *ip;
246326dd49c6SAndrew Gallatin #endif
2464abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6)
246593037a67SJustin Hibbits 	int cap = if_getcapenable(m->m_pkthdr.rcvif);
2466abc5b96bSAndrew Gallatin #endif
246726dd49c6SAndrew Gallatin 	uint16_t c, etype;
246826dd49c6SAndrew Gallatin 
2469053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
247026dd49c6SAndrew Gallatin 	etype = ntohs(eh->ether_type);
247126dd49c6SAndrew Gallatin 	switch (etype) {
2472eb6219e3SAndrew Gallatin #ifdef INET
247326dd49c6SAndrew Gallatin 	case ETHERTYPE_IP:
247426dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM) == 0)
247526dd49c6SAndrew Gallatin 			return (1);
247626dd49c6SAndrew Gallatin 		ip = (struct ip *)(eh + 1);
247726dd49c6SAndrew Gallatin 		if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP)
247826dd49c6SAndrew Gallatin 			return (1);
2479053e637fSAndrew Gallatin 		c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
248026dd49c6SAndrew Gallatin 			      htonl(ntohs(csum) + ntohs(ip->ip_len) -
248126dd49c6SAndrew Gallatin 				    (ip->ip_hl << 2) + ip->ip_p));
2482053e637fSAndrew Gallatin 		c ^= 0xffff;
248326dd49c6SAndrew Gallatin 		break;
248426dd49c6SAndrew Gallatin #endif
248526dd49c6SAndrew Gallatin #ifdef INET6
248626dd49c6SAndrew Gallatin 	case ETHERTYPE_IPV6:
248726dd49c6SAndrew Gallatin 		if ((cap & IFCAP_RXCSUM_IPV6) == 0)
248826dd49c6SAndrew Gallatin 			return (1);
248926dd49c6SAndrew Gallatin 		c = mxge_rx_csum6((eh + 1), m, csum);
249026dd49c6SAndrew Gallatin 		break;
249126dd49c6SAndrew Gallatin #endif
249226dd49c6SAndrew Gallatin 	default:
249326dd49c6SAndrew Gallatin 		c = 1;
249426dd49c6SAndrew Gallatin 	}
2495053e637fSAndrew Gallatin 	return (c);
24965e7d8541SAndrew Gallatin }
2497053e637fSAndrew Gallatin 
2498c792928fSAndrew Gallatin static void
mxge_vlan_tag_remove(struct mbuf * m,uint32_t * csum)2499c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2500c792928fSAndrew Gallatin {
2501c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2502c792928fSAndrew Gallatin 	uint32_t partial;
2503c792928fSAndrew Gallatin 
2504c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2505c792928fSAndrew Gallatin 
2506c792928fSAndrew Gallatin 	/*
2507c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2508c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2509c792928fSAndrew Gallatin 	 * header.
2510c792928fSAndrew Gallatin 	 */
2511c792928fSAndrew Gallatin 
2512c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2513c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2514c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2515c792928fSAndrew Gallatin 	(*csum) += ~partial;
2516c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2517c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2518c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2519c792928fSAndrew Gallatin 
2520c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2521c792928fSAndrew Gallatin 	   later consumers expect this */
2522c792928fSAndrew Gallatin 	*csum = htons(*csum);
2523c792928fSAndrew Gallatin 
2524c792928fSAndrew Gallatin 	/* save the tag */
252537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2526c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
252737d89b0cSAndrew Gallatin #else
252837d89b0cSAndrew Gallatin 	{
252937d89b0cSAndrew Gallatin 		struct m_tag *mtag;
253037d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
253137d89b0cSAndrew Gallatin 				   M_NOWAIT);
253237d89b0cSAndrew Gallatin 		if (mtag == NULL)
253337d89b0cSAndrew Gallatin 			return;
253437d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
253537d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
253637d89b0cSAndrew Gallatin 	}
253737d89b0cSAndrew Gallatin 
253837d89b0cSAndrew Gallatin #endif
253937d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2540c792928fSAndrew Gallatin 
2541c792928fSAndrew Gallatin 	/*
2542c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2543c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2544c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2545c792928fSAndrew Gallatin 	 * type field is already in place.
2546c792928fSAndrew Gallatin 	 */
2547c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2548c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2549c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2550c792928fSAndrew Gallatin }
2551c792928fSAndrew Gallatin 
25525e7d8541SAndrew Gallatin static inline void
mxge_rx_done_big(struct mxge_slice_state * ss,uint32_t len,uint32_t csum,int lro)255326dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len,
255426dd49c6SAndrew Gallatin 		 uint32_t csum, int lro)
2555b2fc195eSAndrew Gallatin {
25561e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
255793037a67SJustin Hibbits 	if_t ifp;
2558053e637fSAndrew Gallatin 	struct mbuf *m;
2559c792928fSAndrew Gallatin 	struct ether_header *eh;
25601e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2561053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2562b2fc195eSAndrew Gallatin 	int idx;
2563b2fc195eSAndrew Gallatin 
25641e413cf9SAndrew Gallatin 	sc = ss->sc;
2565b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
25661e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2567b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2568053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2569b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2570b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2571b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
25721e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2573053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2574f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2575053e637fSAndrew Gallatin 		return;
2576b2fc195eSAndrew Gallatin 	}
2577053e637fSAndrew Gallatin 
2578b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2579b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2580b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2581b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2582b2fc195eSAndrew Gallatin 
2583b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2584b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2585b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2586b2fc195eSAndrew Gallatin 
2587053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2588053e637fSAndrew Gallatin 	 * aligned */
25895e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2590b2fc195eSAndrew Gallatin 
2591053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2592053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
25931e413cf9SAndrew Gallatin 	ss->ipackets++;
2594c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2595c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2596c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2597c792928fSAndrew Gallatin 	}
2598eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2599eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2600eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2601eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2602eacb70baSSepherosa Ziehau 	}
2603b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
260493037a67SJustin Hibbits 	if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
260526dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
260626dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2607053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
260826dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
260926dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
261026dd49c6SAndrew Gallatin 
261126dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
261226dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0)))
261326dd49c6SAndrew Gallatin 			return;
261426dd49c6SAndrew Gallatin #endif
2615b2fc195eSAndrew Gallatin 	}
2616053e637fSAndrew Gallatin 	/* pass the frame up the stack */
261793037a67SJustin Hibbits 	if_input(ifp, m);
2618b2fc195eSAndrew Gallatin }
2619b2fc195eSAndrew Gallatin 
2620b2fc195eSAndrew Gallatin static inline void
mxge_rx_done_small(struct mxge_slice_state * ss,uint32_t len,uint32_t csum,int lro)262126dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len,
262226dd49c6SAndrew Gallatin 		   uint32_t csum, int lro)
2623b2fc195eSAndrew Gallatin {
26241e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
262593037a67SJustin Hibbits 	if_t ifp;
2626c792928fSAndrew Gallatin 	struct ether_header *eh;
2627b2fc195eSAndrew Gallatin 	struct mbuf *m;
26281e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2629b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2630b2fc195eSAndrew Gallatin 	int idx;
2631b2fc195eSAndrew Gallatin 
26321e413cf9SAndrew Gallatin 	sc = ss->sc;
2633b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
26341e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2635b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2636b2fc195eSAndrew Gallatin 	rx->cnt++;
2637b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2638b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2639b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
26401e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2641b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2642f3f040d9SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2643b2fc195eSAndrew Gallatin 		return;
2644b2fc195eSAndrew Gallatin 	}
2645b2fc195eSAndrew Gallatin 
2646b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2647b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2648b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2649b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2650b2fc195eSAndrew Gallatin 
2651b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2652b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2653b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2654b2fc195eSAndrew Gallatin 
2655b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2656b2fc195eSAndrew Gallatin 	 * aligned */
26575e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2658b2fc195eSAndrew Gallatin 
26599b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
26609b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
26611e413cf9SAndrew Gallatin 	ss->ipackets++;
2662c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2663c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2664c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2665c792928fSAndrew Gallatin 	}
2666eacb70baSSepherosa Ziehau 	/* flowid only valid if RSS hashing is enabled */
2667eacb70baSSepherosa Ziehau 	if (sc->num_slices > 1) {
2668eacb70baSSepherosa Ziehau 		m->m_pkthdr.flowid = (ss - sc->ss);
2669eacb70baSSepherosa Ziehau 		M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
2670eacb70baSSepherosa Ziehau 	}
2671b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
267293037a67SJustin Hibbits 	if ((if_getcapenable(ifp) & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) &&
267326dd49c6SAndrew Gallatin 	    (0 == mxge_rx_csum(m, csum))) {
267426dd49c6SAndrew Gallatin 		/* Tell the stack that the  checksum is good */
2675053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
267626dd49c6SAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
267726dd49c6SAndrew Gallatin 			CSUM_DATA_VALID;
267826dd49c6SAndrew Gallatin 
267926dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6)
268026dd49c6SAndrew Gallatin 		if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum)))
268126dd49c6SAndrew Gallatin 			return;
268226dd49c6SAndrew Gallatin #endif
2683053e637fSAndrew Gallatin 	}
2684b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
268593037a67SJustin Hibbits 	if_input(ifp, m);
2686b2fc195eSAndrew Gallatin }
2687b2fc195eSAndrew Gallatin 
2688b2fc195eSAndrew Gallatin static inline void
mxge_clean_rx_done(struct mxge_slice_state * ss)26891e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
26905e7d8541SAndrew Gallatin {
26911e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26925e7d8541SAndrew Gallatin 	int limit = 0;
26935e7d8541SAndrew Gallatin 	uint16_t length;
26945e7d8541SAndrew Gallatin 	uint16_t checksum;
269526dd49c6SAndrew Gallatin 	int lro;
26965e7d8541SAndrew Gallatin 
269793037a67SJustin Hibbits 	lro = if_getcapenable(ss->sc->ifp) & IFCAP_LRO;
26985e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
26995e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
27005e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2701053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2702b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
270326dd49c6SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum, lro);
27045e7d8541SAndrew Gallatin 		else
270526dd49c6SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum, lro);
27065e7d8541SAndrew Gallatin 		rx_done->cnt++;
2707adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
27085e7d8541SAndrew Gallatin 
27095e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2710f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
27115e7d8541SAndrew Gallatin 			break;
2712053e637fSAndrew Gallatin 	}
271326dd49c6SAndrew Gallatin #if defined(INET)  || defined (INET6)
27146dd38b87SSepherosa Ziehau 	tcp_lro_flush_all(&ss->lc);
2715eb6219e3SAndrew Gallatin #endif
27165e7d8541SAndrew Gallatin }
27175e7d8541SAndrew Gallatin 
27185e7d8541SAndrew Gallatin static inline void
mxge_tx_done(struct mxge_slice_state * ss,uint32_t mcp_idx)27191e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2720b2fc195eSAndrew Gallatin {
272193037a67SJustin Hibbits 	if_t ifp __unused;
27221e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2723b2fc195eSAndrew Gallatin 	struct mbuf *m;
2724b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2725f616ebc7SAndrew Gallatin 	int idx;
2726c6cb3e3fSAndrew Gallatin 	int *flags;
2727b2fc195eSAndrew Gallatin 
27281e413cf9SAndrew Gallatin 	tx = &ss->tx;
27291e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
27305e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2731b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2732b2fc195eSAndrew Gallatin 		tx->done++;
2733b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2734b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2735b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2736b2fc195eSAndrew Gallatin 		if (m != NULL) {
273771032832SAndrew Gallatin 			ss->obytes += m->m_pkthdr.len;
273871032832SAndrew Gallatin 			if (m->m_flags & M_MCAST)
273971032832SAndrew Gallatin 				ss->omcasts++;
2740c6cb3e3fSAndrew Gallatin 			ss->opackets++;
2741b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2742b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2743b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2744b2fc195eSAndrew Gallatin 			m_freem(m);
2745b2fc195eSAndrew Gallatin 		}
27465e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
27475e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
27485e7d8541SAndrew Gallatin 			tx->pkt_done++;
27495e7d8541SAndrew Gallatin 		}
2750b2fc195eSAndrew Gallatin 	}
2751b2fc195eSAndrew Gallatin 
2752b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2753b2fc195eSAndrew Gallatin 	   its OK to send packets */
2754c6cb3e3fSAndrew Gallatin 	flags = &ss->if_drv_flags;
27551e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
2756c6cb3e3fSAndrew Gallatin 	if ((*flags) & IFF_DRV_OACTIVE &&
2757c6cb3e3fSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2758c6cb3e3fSAndrew Gallatin 		*(flags) &= ~IFF_DRV_OACTIVE;
27591e413cf9SAndrew Gallatin 		ss->tx.wake++;
27601e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
2761b2fc195eSAndrew Gallatin 	}
2762c6cb3e3fSAndrew Gallatin 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
2763c6cb3e3fSAndrew Gallatin 		/* let the NIC stop polling this queue, since there
2764c6cb3e3fSAndrew Gallatin 		 * are no more transmits pending */
2765c6cb3e3fSAndrew Gallatin 		if (tx->req == tx->done) {
2766c6cb3e3fSAndrew Gallatin 			*tx->send_stop = 1;
2767c6cb3e3fSAndrew Gallatin 			tx->queue_active = 0;
2768c6cb3e3fSAndrew Gallatin 			tx->deactivate++;
2769c6cb3e3fSAndrew Gallatin 			wmb();
2770c6cb3e3fSAndrew Gallatin 		}
2771c6cb3e3fSAndrew Gallatin 	}
2772c6cb3e3fSAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2773b2fc195eSAndrew Gallatin }
2774b2fc195eSAndrew Gallatin 
277501638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] =
2776c587e59fSAndrew Gallatin {
2777c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2778c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2779c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2780c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
278101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2782c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2783c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2784c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2785c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2786c587e59fSAndrew Gallatin };
278701638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] =
278801638550SAndrew Gallatin {
278951bc2092SAndrew Gallatin 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
27904ae3322fSAndrew Gallatin 	{0,		(1 << 7),	"Reserved"},
279101638550SAndrew Gallatin 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
279201638550SAndrew Gallatin 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
279356b67858SAndrew Gallatin 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
279456b67858SAndrew Gallatin 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
279501638550SAndrew Gallatin };
2796c587e59fSAndrew Gallatin 
2797c587e59fSAndrew Gallatin static void
mxge_media_set(mxge_softc_t * sc,int media_type)2798c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type)
2799c587e59fSAndrew Gallatin {
2800c406ad2eSAndrew Gallatin 
2801c406ad2eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type,
2802c406ad2eSAndrew Gallatin 		    0, NULL);
2803c406ad2eSAndrew Gallatin 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
2804c406ad2eSAndrew Gallatin 	sc->current_media = media_type;
2805c406ad2eSAndrew Gallatin 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
2806c587e59fSAndrew Gallatin }
2807c587e59fSAndrew Gallatin 
2808c587e59fSAndrew Gallatin static void
mxge_media_init(mxge_softc_t * sc)2809c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc)
2810c587e59fSAndrew Gallatin {
2811c587e59fSAndrew Gallatin 	char *ptr;
2812c406ad2eSAndrew Gallatin 	int i;
2813c587e59fSAndrew Gallatin 
2814c406ad2eSAndrew Gallatin 	ifmedia_removeall(&sc->media);
2815c406ad2eSAndrew Gallatin 	mxge_media_set(sc, IFM_AUTO);
2816c587e59fSAndrew Gallatin 
2817c587e59fSAndrew Gallatin 	/*
2818c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2819c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2820c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2821c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2822c587e59fSAndrew Gallatin 	 */
2823c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2824c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2825c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2826c406ad2eSAndrew Gallatin 		return;
2827c587e59fSAndrew Gallatin 	}
2828c587e59fSAndrew Gallatin 
2829c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
2830dc15eac0SEd Schouten 		ptr = strchr(ptr, '-');
2831c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2832c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2833c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2834c587e59fSAndrew Gallatin 			return;
2835c587e59fSAndrew Gallatin 		}
2836c587e59fSAndrew Gallatin 	}
283730882b10SAndrew Gallatin 	if (*ptr == 'C' || *(ptr +1) == 'C') {
283801638550SAndrew Gallatin 		/* -C is CX4 */
2839c406ad2eSAndrew Gallatin 		sc->connector = MXGE_CX4;
2840c406ad2eSAndrew Gallatin 		mxge_media_set(sc, IFM_10G_CX4);
2841c406ad2eSAndrew Gallatin 	} else if (*ptr == 'Q') {
284201638550SAndrew Gallatin 		/* -Q is Quad Ribbon Fiber */
2843c406ad2eSAndrew Gallatin 		sc->connector = MXGE_QRF;
2844c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2845c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2846c406ad2eSAndrew Gallatin 	} else if (*ptr == 'R') {
2847c406ad2eSAndrew Gallatin 		/* -R is XFP */
2848c406ad2eSAndrew Gallatin 		sc->connector = MXGE_XFP;
2849c406ad2eSAndrew Gallatin 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
2850c406ad2eSAndrew Gallatin 		/* -S or -2S is SFP+ */
2851c406ad2eSAndrew Gallatin 		sc->connector = MXGE_SFP;
2852c406ad2eSAndrew Gallatin 	} else {
2853c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2854c406ad2eSAndrew Gallatin 	}
2855c587e59fSAndrew Gallatin }
2856c587e59fSAndrew Gallatin 
2857c406ad2eSAndrew Gallatin /*
2858c406ad2eSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2859c406ad2eSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2860c406ad2eSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2861c406ad2eSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2862c406ad2eSAndrew Gallatin  * than in the interrupt handler itself.
2863c406ad2eSAndrew Gallatin  */
2864c406ad2eSAndrew Gallatin static void
mxge_media_probe(mxge_softc_t * sc)2865c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2866c406ad2eSAndrew Gallatin {
2867c406ad2eSAndrew Gallatin 	mxge_cmd_t cmd;
2868c406ad2eSAndrew Gallatin 	char *cage_type;
2869c406ad2eSAndrew Gallatin 
2870c406ad2eSAndrew Gallatin 	struct mxge_media_type *mxge_media_types = NULL;
2871c406ad2eSAndrew Gallatin 	int i, err, ms, mxge_media_type_entries;
2872c406ad2eSAndrew Gallatin 	uint32_t byte;
2873c406ad2eSAndrew Gallatin 
2874c406ad2eSAndrew Gallatin 	sc->need_media_probe = 0;
2875c406ad2eSAndrew Gallatin 
2876c406ad2eSAndrew Gallatin 	if (sc->connector == MXGE_XFP) {
287701638550SAndrew Gallatin 		/* -R is XFP */
287801638550SAndrew Gallatin 		mxge_media_types = mxge_xfp_media_types;
287901638550SAndrew Gallatin 		mxge_media_type_entries =
288073a1170aSPedro F. Giffuni 			nitems(mxge_xfp_media_types);
288101638550SAndrew Gallatin 		byte = MXGE_XFP_COMPLIANCE_BYTE;
288201638550SAndrew Gallatin 		cage_type = "XFP";
2883c406ad2eSAndrew Gallatin 	} else 	if (sc->connector == MXGE_SFP) {
288401638550SAndrew Gallatin 		/* -S or -2S is SFP+ */
288501638550SAndrew Gallatin 		mxge_media_types = mxge_sfp_media_types;
288601638550SAndrew Gallatin 		mxge_media_type_entries =
288773a1170aSPedro F. Giffuni 			nitems(mxge_sfp_media_types);
288801638550SAndrew Gallatin 		cage_type = "SFP+";
288901638550SAndrew Gallatin 		byte = 3;
2890c406ad2eSAndrew Gallatin 	} else {
2891c406ad2eSAndrew Gallatin 		/* nothing to do; media type cannot change */
2892c587e59fSAndrew Gallatin 		return;
2893c587e59fSAndrew Gallatin 	}
2894c587e59fSAndrew Gallatin 
2895c587e59fSAndrew Gallatin 	/*
2896c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2897c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2898c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2899c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2900c587e59fSAndrew Gallatin 	 * a millisecond
2901c587e59fSAndrew Gallatin 	 */
2902c587e59fSAndrew Gallatin 
2903c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
290401638550SAndrew Gallatin 	cmd.data1 = byte;
290501638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
290601638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
2907c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2908c587e59fSAndrew Gallatin 	}
290901638550SAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
291001638550SAndrew Gallatin 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
2911c587e59fSAndrew Gallatin 	}
2912c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2913c587e59fSAndrew Gallatin 		return;
2914c587e59fSAndrew Gallatin 	}
2915c587e59fSAndrew Gallatin 
2916c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
291701638550SAndrew Gallatin 	cmd.data0 = byte;
291801638550SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2919c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2920c587e59fSAndrew Gallatin 		DELAY(1000);
292101638550SAndrew Gallatin 		cmd.data0 = byte;
292201638550SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
2923c587e59fSAndrew Gallatin 	}
2924c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
292501638550SAndrew Gallatin 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
292601638550SAndrew Gallatin 			      cage_type, err, ms);
2927c587e59fSAndrew Gallatin 		return;
2928c587e59fSAndrew Gallatin 	}
2929c587e59fSAndrew Gallatin 
2930c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2931c587e59fSAndrew Gallatin 		if (mxge_verbose)
293201638550SAndrew Gallatin 			device_printf(sc->dev, "%s:%s\n", cage_type,
2933c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2934c406ad2eSAndrew Gallatin 		if (sc->current_media != mxge_media_types[0].flag) {
2935c406ad2eSAndrew Gallatin 			mxge_media_init(sc);
2936c406ad2eSAndrew Gallatin 			mxge_media_set(sc, mxge_media_types[0].flag);
2937c406ad2eSAndrew Gallatin 		}
2938c587e59fSAndrew Gallatin 		return;
2939c587e59fSAndrew Gallatin 	}
294001638550SAndrew Gallatin 	for (i = 1; i < mxge_media_type_entries; i++) {
2941c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2942c587e59fSAndrew Gallatin 			if (mxge_verbose)
294301638550SAndrew Gallatin 				device_printf(sc->dev, "%s:%s\n",
294401638550SAndrew Gallatin 					      cage_type,
2945c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2946c587e59fSAndrew Gallatin 
2947c406ad2eSAndrew Gallatin 			if (sc->current_media != mxge_media_types[i].flag) {
2948c406ad2eSAndrew Gallatin 				mxge_media_init(sc);
2949c406ad2eSAndrew Gallatin 				mxge_media_set(sc, mxge_media_types[i].flag);
2950c406ad2eSAndrew Gallatin 			}
2951c587e59fSAndrew Gallatin 			return;
2952c587e59fSAndrew Gallatin 		}
2953c587e59fSAndrew Gallatin 	}
2954c406ad2eSAndrew Gallatin 	if (mxge_verbose)
2955c406ad2eSAndrew Gallatin 		device_printf(sc->dev, "%s media 0x%x unknown\n",
2956c406ad2eSAndrew Gallatin 			      cage_type, cmd.data0);
2957c587e59fSAndrew Gallatin 
2958c587e59fSAndrew Gallatin 	return;
2959c587e59fSAndrew Gallatin }
2960c587e59fSAndrew Gallatin 
2961b2fc195eSAndrew Gallatin static void
mxge_intr(void * arg)29626d87a65dSAndrew Gallatin mxge_intr(void *arg)
2963b2fc195eSAndrew Gallatin {
29641e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
29651e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
29661e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
29671e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
29681e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
29695e7d8541SAndrew Gallatin 	uint32_t send_done_count;
29705e7d8541SAndrew Gallatin 	uint8_t valid;
2971b2fc195eSAndrew Gallatin 
29725e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
29735e7d8541SAndrew Gallatin 	if (!stats->valid) {
29745e7d8541SAndrew Gallatin 		return;
2975b2fc195eSAndrew Gallatin 	}
29765e7d8541SAndrew Gallatin 	valid = stats->valid;
2977b2fc195eSAndrew Gallatin 
297891ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
29795e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
29805e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
29815e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
29825e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
29835e7d8541SAndrew Gallatin 			stats->valid = 0;
2984dc8731d4SAndrew Gallatin 	} else {
2985dc8731d4SAndrew Gallatin 		stats->valid = 0;
2986dc8731d4SAndrew Gallatin 	}
2987dc8731d4SAndrew Gallatin 
2988dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
29895e7d8541SAndrew Gallatin 	do {
29905e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
29915e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
29925e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
29935e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
2994c6cb3e3fSAndrew Gallatin 			if (send_done_count != tx->pkt_done)
29951e413cf9SAndrew Gallatin 				mxge_tx_done(ss, (int)send_done_count);
29961e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
29975e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2998b2fc195eSAndrew Gallatin 		}
299991ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
300073c7c83fSAndrew Gallatin 			wmb();
30015e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
3002b2fc195eSAndrew Gallatin 
3003c6cb3e3fSAndrew Gallatin 	/* fw link & error stats meaningful only on the first slice */
3004c6cb3e3fSAndrew Gallatin 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
30055e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
30065e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
3007b2fc195eSAndrew Gallatin 			if (sc->link_state) {
30085e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
30095e7d8541SAndrew Gallatin 				if (mxge_verbose)
30105e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
3011b2fc195eSAndrew Gallatin 			} else {
30125e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
30135e7d8541SAndrew Gallatin 				if (mxge_verbose)
30145e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
3015b2fc195eSAndrew Gallatin 			}
3016c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
3017b2fc195eSAndrew Gallatin 		}
3018b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
30191e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
3020b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
30211e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
30225e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
30235e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
30245e7d8541SAndrew Gallatin 		}
3025c587e59fSAndrew Gallatin 
3026c587e59fSAndrew Gallatin 		if (stats->link_down) {
30275e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
3028c587e59fSAndrew Gallatin 			sc->link_state = 0;
3029c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
3030c587e59fSAndrew Gallatin 		}
3031b2fc195eSAndrew Gallatin 	}
3032b2fc195eSAndrew Gallatin 
30335e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
30345e7d8541SAndrew Gallatin 	if (valid & 0x1)
30351e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
30361e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
3037b2fc195eSAndrew Gallatin }
3038b2fc195eSAndrew Gallatin 
3039b2fc195eSAndrew Gallatin static void
mxge_init(void * arg)30406d87a65dSAndrew Gallatin mxge_init(void *arg)
3041b2fc195eSAndrew Gallatin {
30423cae7311SAndrew Gallatin 	mxge_softc_t *sc = arg;
304393037a67SJustin Hibbits 	if_t ifp = sc->ifp;
30443cae7311SAndrew Gallatin 
30453cae7311SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
304693037a67SJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
30473cae7311SAndrew Gallatin 		(void) mxge_open(sc);
30483cae7311SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3049b2fc195eSAndrew Gallatin }
3050b2fc195eSAndrew Gallatin 
3051b2fc195eSAndrew Gallatin static void
mxge_free_slice_mbufs(struct mxge_slice_state * ss)30521e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
30531e413cf9SAndrew Gallatin {
30541e413cf9SAndrew Gallatin 	int i;
30551e413cf9SAndrew Gallatin 
305626dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
305726dd49c6SAndrew Gallatin 	tcp_lro_free(&ss->lc);
305826dd49c6SAndrew Gallatin #endif
30591e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
30601e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
30611e413cf9SAndrew Gallatin 			continue;
30621e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
30631e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
30641e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
30651e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
30661e413cf9SAndrew Gallatin 	}
30671e413cf9SAndrew Gallatin 
30681e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
30691e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
30701e413cf9SAndrew Gallatin 			continue;
30711e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
30721e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
30731e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
30741e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
30751e413cf9SAndrew Gallatin 	}
30761e413cf9SAndrew Gallatin 
30771e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
30781e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30791e413cf9SAndrew Gallatin 		return;
30801e413cf9SAndrew Gallatin 
30811e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30821e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
30831e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
30841e413cf9SAndrew Gallatin 			continue;
30851e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
30861e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
30871e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
30881e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
30891e413cf9SAndrew Gallatin 	}
30901e413cf9SAndrew Gallatin }
30911e413cf9SAndrew Gallatin 
30921e413cf9SAndrew Gallatin static void
mxge_free_mbufs(mxge_softc_t * sc)30936d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
3094b2fc195eSAndrew Gallatin {
30951e413cf9SAndrew Gallatin 	int slice;
30961e413cf9SAndrew Gallatin 
30971e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
30981e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
30991e413cf9SAndrew Gallatin }
31001e413cf9SAndrew Gallatin 
31011e413cf9SAndrew Gallatin static void
mxge_free_slice_rings(struct mxge_slice_state * ss)31021e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
31031e413cf9SAndrew Gallatin {
3104b2fc195eSAndrew Gallatin 	int i;
3105b2fc195eSAndrew Gallatin 
31061e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
31071e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
31081e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
3109b2fc195eSAndrew Gallatin 
31101e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
31111e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
31121e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
31131e413cf9SAndrew Gallatin 
31141e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
31151e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
31161e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
31171e413cf9SAndrew Gallatin 
31181e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
31191e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
31201e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
31211e413cf9SAndrew Gallatin 
31221e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
31231e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
31241e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
31251e413cf9SAndrew Gallatin 
31261e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
31271e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
31281e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
31291e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
31301e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
3131b2fc195eSAndrew Gallatin 			}
31321e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
31331e413cf9SAndrew Gallatin 		}
31341e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
31351e413cf9SAndrew Gallatin 	}
31361e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
31371e413cf9SAndrew Gallatin 
31381e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
31391e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
31401e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
31411e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
31421e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
31431e413cf9SAndrew Gallatin 			}
31441e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
31451e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
31461e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
31471e413cf9SAndrew Gallatin 		}
31481e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
31491e413cf9SAndrew Gallatin 	}
31501e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
31511e413cf9SAndrew Gallatin 
31521e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
31531e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
31541e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
31551e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
31561e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
31571e413cf9SAndrew Gallatin 			}
31581e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
31591e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
31601e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
31611e413cf9SAndrew Gallatin 		}
31621e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
31631e413cf9SAndrew Gallatin 	}
31641e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
3165b2fc195eSAndrew Gallatin }
3166b2fc195eSAndrew Gallatin 
3167b2fc195eSAndrew Gallatin static void
mxge_free_rings(mxge_softc_t * sc)31686d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
3169b2fc195eSAndrew Gallatin {
31701e413cf9SAndrew Gallatin 	int slice;
3171b2fc195eSAndrew Gallatin 
31721e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
31731e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
3174c2657176SAndrew Gallatin }
3175b2fc195eSAndrew Gallatin 
3176b2fc195eSAndrew Gallatin static int
mxge_alloc_slice_rings(struct mxge_slice_state * ss,int rx_ring_entries,int tx_ring_entries)31771e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31781e413cf9SAndrew Gallatin 		       int tx_ring_entries)
3179b2fc195eSAndrew Gallatin {
31801e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
31811e413cf9SAndrew Gallatin 	size_t bytes;
31821e413cf9SAndrew Gallatin 	int err, i;
3183b2fc195eSAndrew Gallatin 
31841e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
3185adae7080SAndrew Gallatin 
31861e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31871e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
3188aed8e389SAndrew Gallatin 
3189b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
31901e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
31911e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3192b2fc195eSAndrew Gallatin 
31931e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
31941e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3195b2fc195eSAndrew Gallatin 
31961e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
31971e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
31981e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3199b2fc195eSAndrew Gallatin 
32001e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
32011e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
3202b2fc195eSAndrew Gallatin 
32031e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
3204b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3205b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3206b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3207b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3208b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3209b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3210b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
3211b2fc195eSAndrew Gallatin 				 1,			/* num segs */
3212b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
3213b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3214b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32151e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
3216b2fc195eSAndrew Gallatin 	if (err != 0) {
3217b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
3218b2fc195eSAndrew Gallatin 			      err);
3219c2ede4b3SMartin Blapp 		return err;
3220b2fc195eSAndrew Gallatin 	}
3221b2fc195eSAndrew Gallatin 
3222b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
3223b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3224b0f7b922SAndrew Gallatin 				 0,			/* boundary */
3225b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3226b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3227b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3228053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
3229b0f7b922SAndrew Gallatin 				 1,			/* num segs */
3230b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
3231b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
3232b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
32331e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
3234b2fc195eSAndrew Gallatin 	if (err != 0) {
3235b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
3236b2fc195eSAndrew Gallatin 			      err);
3237c2ede4b3SMartin Blapp 		return err;
3238b2fc195eSAndrew Gallatin 	}
32391e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
32401e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32411e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
3242b2fc195eSAndrew Gallatin 		if (err != 0) {
3243b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
3244b2fc195eSAndrew Gallatin 				      err);
3245c2ede4b3SMartin Blapp 			return err;
3246b2fc195eSAndrew Gallatin 		}
3247b2fc195eSAndrew Gallatin 	}
32481e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32491e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
3250b2fc195eSAndrew Gallatin 	if (err != 0) {
3251b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
3252b2fc195eSAndrew Gallatin 			      err);
3253c2ede4b3SMartin Blapp 		return err;
3254b2fc195eSAndrew Gallatin 	}
3255b2fc195eSAndrew Gallatin 
32561e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32571e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32581e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
3259b2fc195eSAndrew Gallatin 		if (err != 0) {
3260b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
3261b2fc195eSAndrew Gallatin 				      err);
3262c2ede4b3SMartin Blapp 			return err;
3263b2fc195eSAndrew Gallatin 		}
3264b2fc195eSAndrew Gallatin 	}
32651e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32661e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
3267b2fc195eSAndrew Gallatin 	if (err != 0) {
3268b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
3269b2fc195eSAndrew Gallatin 			      err);
3270c2ede4b3SMartin Blapp 		return err;
32711e413cf9SAndrew Gallatin 	}
32721e413cf9SAndrew Gallatin 
3273b78540b1SGabor Kovesdan 	/* now allocate TX resources */
32741e413cf9SAndrew Gallatin 
32751e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
32761e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32771e413cf9SAndrew Gallatin 
32781e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
32791e413cf9SAndrew Gallatin 	bytes = 8 +
32801e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
32811e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
32821e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
32831e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
3284aa54c242SJohn Baldwin 		((uintptr_t)(ss->tx.req_bytes + 7) & ~7UL);
32851e413cf9SAndrew Gallatin 
32861e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
32871e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32881e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
32891e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
32901e413cf9SAndrew Gallatin 
32911e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
32921e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
32931e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32941e413cf9SAndrew Gallatin 
32951e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
32961e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
32971e413cf9SAndrew Gallatin 				 1,			/* alignment */
32981e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
32991e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
33001e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
33011e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
33021e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
33031e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
33041e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
33051e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
33061e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
33071e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
33081e413cf9SAndrew Gallatin 
33091e413cf9SAndrew Gallatin 	if (err != 0) {
33101e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
33111e413cf9SAndrew Gallatin 			      err);
3312c2ede4b3SMartin Blapp 		return err;
33131e413cf9SAndrew Gallatin 	}
33141e413cf9SAndrew Gallatin 
33151e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
33161e413cf9SAndrew Gallatin 	   in the ring */
33171e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
33181e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
33191e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
33201e413cf9SAndrew Gallatin 		if (err != 0) {
33211e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33221e413cf9SAndrew Gallatin 				      err);
3323c2ede4b3SMartin Blapp 			return err;
33241e413cf9SAndrew Gallatin 		}
3325b2fc195eSAndrew Gallatin 	}
3326b2fc195eSAndrew Gallatin 	return 0;
3327b2fc195eSAndrew Gallatin 
3328b2fc195eSAndrew Gallatin }
3329b2fc195eSAndrew Gallatin 
33301e413cf9SAndrew Gallatin static int
mxge_alloc_rings(mxge_softc_t * sc)33311e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
33321e413cf9SAndrew Gallatin {
33331e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
33341e413cf9SAndrew Gallatin 	int tx_ring_size;
33351e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
33361e413cf9SAndrew Gallatin 	int err, slice;
33371e413cf9SAndrew Gallatin 
33381e413cf9SAndrew Gallatin 	/* get ring sizes */
33391e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33401e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
33411e413cf9SAndrew Gallatin 	if (err != 0) {
33421e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33431e413cf9SAndrew Gallatin 		goto abort;
33441e413cf9SAndrew Gallatin 	}
33451e413cf9SAndrew Gallatin 
33461e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33471e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
334893037a67SJustin Hibbits 	if_setsendqlen(sc->ifp, tx_ring_entries - 1);
334993037a67SJustin Hibbits 	if_setsendqready(sc->ifp);
33501e413cf9SAndrew Gallatin 
33511e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33521e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33531e413cf9SAndrew Gallatin 					     rx_ring_entries,
33541e413cf9SAndrew Gallatin 					     tx_ring_entries);
33551e413cf9SAndrew Gallatin 		if (err != 0)
33561e413cf9SAndrew Gallatin 			goto abort;
33571e413cf9SAndrew Gallatin 	}
33581e413cf9SAndrew Gallatin 	return 0;
33591e413cf9SAndrew Gallatin 
33601e413cf9SAndrew Gallatin abort:
33611e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
33621e413cf9SAndrew Gallatin 	return err;
33631e413cf9SAndrew Gallatin 
33641e413cf9SAndrew Gallatin }
33651e413cf9SAndrew Gallatin 
3366053e637fSAndrew Gallatin static void
mxge_choose_params(int mtu,int * big_buf_size,int * cl_size,int * nbufs)3367053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3368053e637fSAndrew Gallatin {
3369c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3370053e637fSAndrew Gallatin 
3371053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3372053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3373053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3374053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3375053e637fSAndrew Gallatin 		*nbufs = 1;
3376053e637fSAndrew Gallatin 		return;
3377053e637fSAndrew Gallatin 	}
3378053e637fSAndrew Gallatin 
3379053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3380053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3381053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3382053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3383053e637fSAndrew Gallatin 		*nbufs = 1;
3384053e637fSAndrew Gallatin 		return;
3385053e637fSAndrew Gallatin 	}
3386b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3387b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3388b0f7b922SAndrew Gallatin 	*nbufs = 1;
3389053e637fSAndrew Gallatin }
3390053e637fSAndrew Gallatin 
3391b2fc195eSAndrew Gallatin static int
mxge_slice_open(struct mxge_slice_state * ss,int nbufs,int cl_size)33921e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3393b2fc195eSAndrew Gallatin {
33941e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
33956d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3396b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
33971e413cf9SAndrew Gallatin 	int err, i, slice;
3398b2fc195eSAndrew Gallatin 
33991e413cf9SAndrew Gallatin 	sc = ss->sc;
34001e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
34011e413cf9SAndrew Gallatin 
340226dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
340326dd49c6SAndrew Gallatin 	(void)tcp_lro_init(&ss->lc);
340426dd49c6SAndrew Gallatin #endif
340526dd49c6SAndrew Gallatin 	ss->lc.ifp = sc->ifp;
3406053e637fSAndrew Gallatin 
34071e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
34081e413cf9SAndrew Gallatin 
34091e413cf9SAndrew Gallatin 	err = 0;
34106147584bSElliott Mitchell 
34111e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34121e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34131e413cf9SAndrew Gallatin 	ss->tx.lanai =
34141e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
3415c6cb3e3fSAndrew Gallatin 	ss->tx.send_go = (volatile uint32_t *)
3416c6cb3e3fSAndrew Gallatin 		(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3417c6cb3e3fSAndrew Gallatin 	ss->tx.send_stop = (volatile uint32_t *)
3418c6cb3e3fSAndrew Gallatin 	(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
34196147584bSElliott Mitchell 
34201e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34211e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
34221e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34231e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
34241e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34251e413cf9SAndrew Gallatin 	cmd.data0 = slice;
34261e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34271e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
34281e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34291e413cf9SAndrew Gallatin 
34301e413cf9SAndrew Gallatin 	if (err != 0) {
34311e413cf9SAndrew Gallatin 		device_printf(sc->dev,
34321e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
34331e413cf9SAndrew Gallatin 		return EIO;
34341e413cf9SAndrew Gallatin 	}
34351e413cf9SAndrew Gallatin 
34361e413cf9SAndrew Gallatin 	/* stock receive rings */
34371e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
34381e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
34391e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
34401e413cf9SAndrew Gallatin 		if (err) {
34411e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34421e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
34431e413cf9SAndrew Gallatin 			return ENOMEM;
34441e413cf9SAndrew Gallatin 		}
34451e413cf9SAndrew Gallatin 	}
34461e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
34471e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34481e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34491e413cf9SAndrew Gallatin 	}
34501e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
34511e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
345293037a67SJustin Hibbits 	ss->rx_big.mlen = if_getmtu(ss->sc->ifp) + ETHER_HDR_LEN +
34534d9a5852SAndrew Gallatin 		ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
34541e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34551e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
34561e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
34571e413cf9SAndrew Gallatin 		if (err) {
34581e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34591e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
34601e413cf9SAndrew Gallatin 			return ENOMEM;
34611e413cf9SAndrew Gallatin 		}
34621e413cf9SAndrew Gallatin 	}
34631e413cf9SAndrew Gallatin 	return 0;
34641e413cf9SAndrew Gallatin }
34651e413cf9SAndrew Gallatin 
34661e413cf9SAndrew Gallatin static int
mxge_open(mxge_softc_t * sc)34671e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
34681e413cf9SAndrew Gallatin {
34691e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
34701e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
34711e413cf9SAndrew Gallatin 	bus_addr_t bus;
34721e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3473c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3474b2fc195eSAndrew Gallatin 
34757d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
347693037a67SJustin Hibbits 	bcopy(if_getlladdr(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34777d542e2dSAndrew Gallatin 
3478adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3479b2fc195eSAndrew Gallatin 	if (err != 0) {
3480b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3481b2fc195eSAndrew Gallatin 		return EIO;
3482b2fc195eSAndrew Gallatin 	}
3483b2fc195eSAndrew Gallatin 
34841e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
34851e413cf9SAndrew Gallatin 		/* setup the indirection table */
34861e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
34871e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34881e413cf9SAndrew Gallatin 				    &cmd);
3489b2fc195eSAndrew Gallatin 
34901e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34911e413cf9SAndrew Gallatin 				     &cmd);
34921e413cf9SAndrew Gallatin 		if (err != 0) {
34931e413cf9SAndrew Gallatin 			device_printf(sc->dev,
34941e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
34951e413cf9SAndrew Gallatin 			return err;
34961e413cf9SAndrew Gallatin 		}
34971e413cf9SAndrew Gallatin 
34981e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
34991e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
35001e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
35011e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
35021e413cf9SAndrew Gallatin 
35031e413cf9SAndrew Gallatin 		cmd.data0 = 1;
35041e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
35051e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35061e413cf9SAndrew Gallatin 		if (err != 0) {
35071e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
35081e413cf9SAndrew Gallatin 			return err;
35091e413cf9SAndrew Gallatin 		}
35101e413cf9SAndrew Gallatin 	}
35111e413cf9SAndrew Gallatin 
351293037a67SJustin Hibbits 	mxge_choose_params(if_getmtu(sc->ifp), &big_bytes, &cl_size, &nbufs);
35131e413cf9SAndrew Gallatin 
35141e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3515053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3516053e637fSAndrew Gallatin 			    &cmd);
3517053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3518053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35191e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3520053e637fSAndrew Gallatin 		device_printf(sc->dev,
3521053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
35221e413cf9SAndrew Gallatin 			      nbufs);
3523053e637fSAndrew Gallatin 		return EIO;
3524053e637fSAndrew Gallatin 	}
3525b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3526b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3527b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
352893037a67SJustin Hibbits 	cmd.data0 = if_getmtu(sc->ifp) + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
35295e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3530b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
35315e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3532b2fc195eSAndrew Gallatin 			     &cmd);
3533053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
35345e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35350fa7f681SAndrew Gallatin 
35360fa7f681SAndrew Gallatin 	if (err != 0) {
35370fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
35380fa7f681SAndrew Gallatin 		goto abort;
35390fa7f681SAndrew Gallatin 	}
35400fa7f681SAndrew Gallatin 
3541b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
35426147584bSElliott Mitchell 	for (slice = 0; slice < sc->num_slices; slice++) {
3543c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3544c6cb3e3fSAndrew Gallatin 		cmd.data0 =
3545c6cb3e3fSAndrew Gallatin 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
3546c6cb3e3fSAndrew Gallatin 		cmd.data1 =
3547c6cb3e3fSAndrew Gallatin 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35480fa7f681SAndrew Gallatin 		cmd.data2 = sizeof(struct mcp_irq_data);
3549c6cb3e3fSAndrew Gallatin 		cmd.data2 |= (slice << 16);
3550c6cb3e3fSAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
3551c6cb3e3fSAndrew Gallatin 	}
35520fa7f681SAndrew Gallatin 
35530fa7f681SAndrew Gallatin 	if (err != 0) {
35541e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
35550fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
35560fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35570fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35580fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
35590fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35600fa7f681SAndrew Gallatin 				    &cmd);
35610fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
35620fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
35630fa7f681SAndrew Gallatin 	} else {
35640fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
35650fa7f681SAndrew Gallatin 	}
3566b2fc195eSAndrew Gallatin 
3567b2fc195eSAndrew Gallatin 	if (err != 0) {
3568b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3569b2fc195eSAndrew Gallatin 		goto abort;
3570b2fc195eSAndrew Gallatin 	}
3571b2fc195eSAndrew Gallatin 
35721e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
35731e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35741e413cf9SAndrew Gallatin 		if (err != 0) {
35751e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
35761e413cf9SAndrew Gallatin 				      slice);
35771e413cf9SAndrew Gallatin 			goto abort;
35781e413cf9SAndrew Gallatin 		}
35791e413cf9SAndrew Gallatin 	}
35801e413cf9SAndrew Gallatin 
3581b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
35825e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3583b2fc195eSAndrew Gallatin 	if (err) {
3584b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3585b2fc195eSAndrew Gallatin 		goto abort;
3586b2fc195eSAndrew Gallatin 	}
3587c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3588c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3589c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags |= IFF_DRV_RUNNING;
3590c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_OACTIVE;
3591c6cb3e3fSAndrew Gallatin 	}
359293037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, IFF_DRV_RUNNING, 0);
359393037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE);
3594b2fc195eSAndrew Gallatin 
3595b2fc195eSAndrew Gallatin 	return 0;
3596b2fc195eSAndrew Gallatin 
3597b2fc195eSAndrew Gallatin abort:
35986d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3599a98d6cd7SAndrew Gallatin 
3600b2fc195eSAndrew Gallatin 	return err;
3601b2fc195eSAndrew Gallatin }
3602b2fc195eSAndrew Gallatin 
3603b2fc195eSAndrew Gallatin static int
mxge_close(mxge_softc_t * sc,int down)3604a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down)
3605b2fc195eSAndrew Gallatin {
36066d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3607b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3608c6cb3e3fSAndrew Gallatin 	struct mxge_slice_state *ss;
3609c6cb3e3fSAndrew Gallatin 	int slice;
3610b2fc195eSAndrew Gallatin 
3611c6cb3e3fSAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
3612c6cb3e3fSAndrew Gallatin 		ss = &sc->ss[slice];
3613c6cb3e3fSAndrew Gallatin 		ss->if_drv_flags &= ~IFF_DRV_RUNNING;
3614c6cb3e3fSAndrew Gallatin 	}
361593037a67SJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, IFF_DRV_RUNNING);
3616a393336bSAndrew Gallatin 	if (!down) {
3617b2fc195eSAndrew Gallatin 		old_down_cnt = sc->down_cnt;
361873c7c83fSAndrew Gallatin 		wmb();
36195e7d8541SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3620b2fc195eSAndrew Gallatin 		if (err) {
3621a393336bSAndrew Gallatin 			device_printf(sc->dev,
3622a393336bSAndrew Gallatin 				      "Couldn't bring down link\n");
3623b2fc195eSAndrew Gallatin 		}
3624b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3625b2fc195eSAndrew Gallatin 			/* wait for down irq */
3626dce01b9bSAndrew Gallatin 			DELAY(10 * sc->intr_coal_delay);
3627b2fc195eSAndrew Gallatin 		}
362873c7c83fSAndrew Gallatin 		wmb();
3629b2fc195eSAndrew Gallatin 		if (old_down_cnt == sc->down_cnt) {
3630b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "never got down irq\n");
3631b2fc195eSAndrew Gallatin 		}
3632a393336bSAndrew Gallatin 	}
36336d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3634a98d6cd7SAndrew Gallatin 
3635b2fc195eSAndrew Gallatin 	return 0;
3636b2fc195eSAndrew Gallatin }
3637b2fc195eSAndrew Gallatin 
3638dce01b9bSAndrew Gallatin static void
mxge_setup_cfg_space(mxge_softc_t * sc)3639dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3640dce01b9bSAndrew Gallatin {
3641dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3642dce01b9bSAndrew Gallatin 	int reg;
3643c68534f1SScott Long 	uint16_t lnk, pectl;
3644dce01b9bSAndrew Gallatin 
3645dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
36463b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_EXPRESS, &reg) == 0) {
3647dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3648dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3649dce01b9bSAndrew Gallatin 
365083d54b59SAndrew Gallatin 		if (sc->pectl == 0) {
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);
365483d54b59SAndrew Gallatin 			sc->pectl = pectl;
365583d54b59SAndrew Gallatin 		} else {
365683d54b59SAndrew Gallatin 			/* restore saved pectl after watchdog reset */
365783d54b59SAndrew Gallatin 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
365883d54b59SAndrew Gallatin 		}
3659dce01b9bSAndrew Gallatin 	}
3660dce01b9bSAndrew Gallatin 
3661dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3662dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3663dce01b9bSAndrew Gallatin }
3664dce01b9bSAndrew Gallatin 
3665dce01b9bSAndrew Gallatin static uint32_t
mxge_read_reboot(mxge_softc_t * sc)3666dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3667dce01b9bSAndrew Gallatin {
3668dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3669dce01b9bSAndrew Gallatin 	uint32_t vs;
3670dce01b9bSAndrew Gallatin 
3671dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
36723b0a4aefSJohn Baldwin 	if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) {
3673dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3674dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3675dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3676dce01b9bSAndrew Gallatin 	}
3677dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3678dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3679dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3680dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3681dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3682dce01b9bSAndrew Gallatin }
3683dce01b9bSAndrew Gallatin 
368472c042dfSAndrew Gallatin static void
mxge_watchdog_reset(mxge_softc_t * sc)368572c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3686dce01b9bSAndrew Gallatin {
3687e749ef6bSAndrew Gallatin 	struct pci_devinfo *dinfo;
3688a393336bSAndrew Gallatin 	struct mxge_slice_state *ss;
3689a393336bSAndrew Gallatin 	int err, running, s, num_tx_slices = 1;
3690dce01b9bSAndrew Gallatin 	uint32_t reboot;
3691dce01b9bSAndrew Gallatin 	uint16_t cmd;
3692dce01b9bSAndrew Gallatin 
3693dce01b9bSAndrew Gallatin 	err = ENXIO;
3694dce01b9bSAndrew Gallatin 
3695dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3696dce01b9bSAndrew Gallatin 
3697dce01b9bSAndrew Gallatin 	/*
3698dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3699dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3700dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3701dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3702dce01b9bSAndrew Gallatin 	 * again
3703dce01b9bSAndrew Gallatin 	 */
3704dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3705dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3706dce01b9bSAndrew Gallatin 		/*
3707dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3708dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3709dce01b9bSAndrew Gallatin 		 * back, then give up
3710dce01b9bSAndrew Gallatin 		 */
3711dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3712dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3713dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3714dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3715dce01b9bSAndrew Gallatin 		}
3716dce01b9bSAndrew Gallatin 	}
3717dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3718dce01b9bSAndrew Gallatin 		/* print the reboot status */
3719dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3720dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3721dce01b9bSAndrew Gallatin 			      reboot);
372293037a67SJustin Hibbits 		running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
3723a393336bSAndrew Gallatin 		if (running) {
3724a393336bSAndrew Gallatin 			/*
3725a393336bSAndrew Gallatin 			 * quiesce NIC so that TX routines will not try to
3726a393336bSAndrew Gallatin 			 * xmit after restoration of BAR
3727a393336bSAndrew Gallatin 			 */
3728a393336bSAndrew Gallatin 
3729a393336bSAndrew Gallatin 			/* Mark the link as down */
3730a393336bSAndrew Gallatin 			if (sc->link_state) {
3731a393336bSAndrew Gallatin 				sc->link_state = 0;
3732a393336bSAndrew Gallatin 				if_link_state_change(sc->ifp,
3733a393336bSAndrew Gallatin 						     LINK_STATE_DOWN);
3734a393336bSAndrew Gallatin 			}
37356147584bSElliott Mitchell 
3736a393336bSAndrew Gallatin 			num_tx_slices = sc->num_slices;
37376147584bSElliott Mitchell 
3738a393336bSAndrew Gallatin 			/* grab all TX locks to ensure no tx  */
3739a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3740a393336bSAndrew Gallatin 				ss = &sc->ss[s];
3741a393336bSAndrew Gallatin 				mtx_lock(&ss->tx.mtx);
3742a393336bSAndrew Gallatin 			}
3743a393336bSAndrew Gallatin 			mxge_close(sc, 1);
3744a393336bSAndrew Gallatin 		}
3745dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3746e749ef6bSAndrew Gallatin 		dinfo = device_get_ivars(sc->dev);
3747e749ef6bSAndrew Gallatin 		pci_cfg_restore(sc->dev, dinfo);
3748dce01b9bSAndrew Gallatin 
3749dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3750dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
375110882804SAndrew Gallatin 
3752a393336bSAndrew Gallatin 		/* reload f/w */
3753a393336bSAndrew Gallatin 		err = mxge_load_firmware(sc, 0);
3754a393336bSAndrew Gallatin 		if (err) {
3755a393336bSAndrew Gallatin 			device_printf(sc->dev,
3756a393336bSAndrew Gallatin 				      "Unable to re-load f/w\n");
375710882804SAndrew Gallatin 		}
3758a393336bSAndrew Gallatin 		if (running) {
3759a393336bSAndrew Gallatin 			if (!err)
3760a393336bSAndrew Gallatin 				err = mxge_open(sc);
3761a393336bSAndrew Gallatin 			/* release all TX locks */
3762a393336bSAndrew Gallatin 			for (s = 0; s < num_tx_slices; s++) {
3763a393336bSAndrew Gallatin 				ss = &sc->ss[s];
376483d54b59SAndrew Gallatin 				mxge_start_locked(ss);
3765a393336bSAndrew Gallatin 				mtx_unlock(&ss->tx.mtx);
3766a393336bSAndrew Gallatin 			}
3767a393336bSAndrew Gallatin 		}
3768a393336bSAndrew Gallatin 		sc->watchdog_resets++;
3769dce01b9bSAndrew Gallatin 	} else {
3770c6cb3e3fSAndrew Gallatin 		device_printf(sc->dev,
377172c042dfSAndrew Gallatin 			      "NIC did not reboot, not resetting\n");
377272c042dfSAndrew Gallatin 		err = 0;
377372c042dfSAndrew Gallatin 	}
377472c042dfSAndrew Gallatin 	if (err) {
377572c042dfSAndrew Gallatin 		device_printf(sc->dev, "watchdog reset failed\n");
377672c042dfSAndrew Gallatin 	} else {
37776b484a49SAndrew Gallatin 		if (sc->dying == 2)
37786b484a49SAndrew Gallatin 			sc->dying = 0;
37796b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
378072c042dfSAndrew Gallatin 	}
378172c042dfSAndrew Gallatin }
378272c042dfSAndrew Gallatin 
378372c042dfSAndrew Gallatin static void
mxge_watchdog_task(void * arg,int pending)378472c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending)
378572c042dfSAndrew Gallatin {
378672c042dfSAndrew Gallatin 	mxge_softc_t *sc = arg;
378772c042dfSAndrew Gallatin 
378872c042dfSAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
378972c042dfSAndrew Gallatin 	mxge_watchdog_reset(sc);
379072c042dfSAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
379172c042dfSAndrew Gallatin }
379272c042dfSAndrew Gallatin 
379372c042dfSAndrew Gallatin static void
mxge_warn_stuck(mxge_softc_t * sc,mxge_tx_ring_t * tx,int slice)379472c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
379572c042dfSAndrew Gallatin {
379672c042dfSAndrew Gallatin 	tx = &sc->ss[slice].tx;
379772c042dfSAndrew Gallatin 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
3798c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev,
3799c6cb3e3fSAndrew Gallatin 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
3800c6cb3e3fSAndrew Gallatin 		      tx->req, tx->done, tx->queue_active);
3801c6cb3e3fSAndrew Gallatin 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
3802c6cb3e3fSAndrew Gallatin 			      tx->activate, tx->deactivate);
3803dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
3804c6cb3e3fSAndrew Gallatin 		      tx->pkt_done,
38051e413cf9SAndrew Gallatin 		      be32toh(sc->ss->fw_stats->send_done_count));
3806dce01b9bSAndrew Gallatin }
3807dce01b9bSAndrew Gallatin 
3808e749ef6bSAndrew Gallatin static int
mxge_watchdog(mxge_softc_t * sc)3809dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3810dce01b9bSAndrew Gallatin {
3811c6cb3e3fSAndrew Gallatin 	mxge_tx_ring_t *tx;
38121e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3813c6cb3e3fSAndrew Gallatin 	int i, err = 0;
3814dce01b9bSAndrew Gallatin 
3815dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3816dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
38176147584bSElliott Mitchell 	for (i = 0; (i < sc->num_slices) && (err == 0); i++) {
3818c6cb3e3fSAndrew Gallatin 		tx = &sc->ss[i].tx;
3819dce01b9bSAndrew Gallatin 		if (tx->req != tx->done &&
3820dce01b9bSAndrew Gallatin 		    tx->watchdog_req != tx->watchdog_done &&
3821c587e59fSAndrew Gallatin 		    tx->done == tx->watchdog_done) {
3822c587e59fSAndrew Gallatin 			/* check for pause blocking before resetting */
382372c042dfSAndrew Gallatin 			if (tx->watchdog_rx_pause == rx_pause) {
382472c042dfSAndrew Gallatin 				mxge_warn_stuck(sc, tx, i);
382572c042dfSAndrew Gallatin 				taskqueue_enqueue(sc->tq, &sc->watchdog_task);
382672c042dfSAndrew Gallatin 				return (ENXIO);
382772c042dfSAndrew Gallatin 			}
3828c587e59fSAndrew Gallatin 			else
3829c587e59fSAndrew Gallatin 				device_printf(sc->dev, "Flow control blocking "
3830c587e59fSAndrew Gallatin 					      "xmits, check link partner\n");
3831c587e59fSAndrew Gallatin 		}
3832dce01b9bSAndrew Gallatin 
3833dce01b9bSAndrew Gallatin 		tx->watchdog_req = tx->req;
3834dce01b9bSAndrew Gallatin 		tx->watchdog_done = tx->done;
3835c587e59fSAndrew Gallatin 		tx->watchdog_rx_pause = rx_pause;
3836c6cb3e3fSAndrew Gallatin 	}
3837c587e59fSAndrew Gallatin 
3838c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3839c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3840e749ef6bSAndrew Gallatin 	return (err);
3841dce01b9bSAndrew Gallatin }
3842dce01b9bSAndrew Gallatin 
3843f3f040d9SGleb Smirnoff static uint64_t
mxge_get_counter(if_t ifp,ift_counter cnt)384493037a67SJustin Hibbits mxge_get_counter(if_t ifp, ift_counter cnt)
38451e413cf9SAndrew Gallatin {
3846f3f040d9SGleb Smirnoff 	struct mxge_softc *sc;
3847f3f040d9SGleb Smirnoff 	uint64_t rv;
38481e413cf9SAndrew Gallatin 
3849f3f040d9SGleb Smirnoff 	sc = if_getsoftc(ifp);
3850f3f040d9SGleb Smirnoff 	rv = 0;
3851f3f040d9SGleb Smirnoff 
3852f3f040d9SGleb Smirnoff 	switch (cnt) {
3853f3f040d9SGleb Smirnoff 	case IFCOUNTER_IPACKETS:
3854f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3855f3f040d9SGleb Smirnoff 			rv += sc->ss[s].ipackets;
3856f3f040d9SGleb Smirnoff 		return (rv);
3857f3f040d9SGleb Smirnoff 	case IFCOUNTER_OPACKETS:
3858f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3859f3f040d9SGleb Smirnoff 			rv += sc->ss[s].opackets;
3860f3f040d9SGleb Smirnoff 		return (rv);
3861f3f040d9SGleb Smirnoff 	case IFCOUNTER_OERRORS:
3862f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3863f3f040d9SGleb Smirnoff 			rv += sc->ss[s].oerrors;
3864f3f040d9SGleb Smirnoff 		return (rv);
3865f3f040d9SGleb Smirnoff 	case IFCOUNTER_OBYTES:
3866f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3867f3f040d9SGleb Smirnoff 			rv += sc->ss[s].obytes;
3868f3f040d9SGleb Smirnoff 		return (rv);
3869f3f040d9SGleb Smirnoff 	case IFCOUNTER_OMCASTS:
3870f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3871f3f040d9SGleb Smirnoff 			rv += sc->ss[s].omcasts;
3872f3f040d9SGleb Smirnoff 		return (rv);
3873f3f040d9SGleb Smirnoff 	case IFCOUNTER_OQDROPS:
3874f3f040d9SGleb Smirnoff 		for (int s = 0; s < sc->num_slices; s++)
3875f3f040d9SGleb Smirnoff 			rv += sc->ss[s].tx.br->br_drops;
3876f3f040d9SGleb Smirnoff 		return (rv);
3877f3f040d9SGleb Smirnoff 	default:
3878f3f040d9SGleb Smirnoff 		return (if_get_counter_default(ifp, cnt));
38791e413cf9SAndrew Gallatin 	}
38801e413cf9SAndrew Gallatin }
3881c6cb3e3fSAndrew Gallatin 
38821e413cf9SAndrew Gallatin static void
mxge_tick(void * arg)3883dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3884dce01b9bSAndrew Gallatin {
3885dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
38866b484a49SAndrew Gallatin 	u_long pkts = 0;
3887e749ef6bSAndrew Gallatin 	int err = 0;
38886b484a49SAndrew Gallatin 	int running, ticks;
38896b484a49SAndrew Gallatin 	uint16_t cmd;
3890dce01b9bSAndrew Gallatin 
38916b484a49SAndrew Gallatin 	ticks = mxge_ticks;
389293037a67SJustin Hibbits 	running = if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING;
38936b484a49SAndrew Gallatin 	if (running) {
38941e413cf9SAndrew Gallatin 		if (!sc->watchdog_countdown) {
3895e749ef6bSAndrew Gallatin 			err = mxge_watchdog(sc);
38961e413cf9SAndrew Gallatin 			sc->watchdog_countdown = 4;
38971e413cf9SAndrew Gallatin 		}
38981e413cf9SAndrew Gallatin 		sc->watchdog_countdown--;
38996b484a49SAndrew Gallatin 	}
39006b484a49SAndrew Gallatin 	if (pkts == 0) {
39016b484a49SAndrew Gallatin 		/* ensure NIC did not suffer h/w fault while idle */
39026b484a49SAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
39036b484a49SAndrew Gallatin 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
39046b484a49SAndrew Gallatin 			sc->dying = 2;
39056b484a49SAndrew Gallatin 			taskqueue_enqueue(sc->tq, &sc->watchdog_task);
39066b484a49SAndrew Gallatin 			err = ENXIO;
39076b484a49SAndrew Gallatin 		}
39086b484a49SAndrew Gallatin 		/* look less often if NIC is idle */
39096b484a49SAndrew Gallatin 		ticks *= 4;
39106b484a49SAndrew Gallatin 	}
39116b484a49SAndrew Gallatin 
3912e749ef6bSAndrew Gallatin 	if (err == 0)
39136b484a49SAndrew Gallatin 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
3914e749ef6bSAndrew Gallatin 
3915dce01b9bSAndrew Gallatin }
3916b2fc195eSAndrew Gallatin 
3917b2fc195eSAndrew Gallatin static int
mxge_media_change(if_t ifp)391893037a67SJustin Hibbits mxge_media_change(if_t ifp)
3919b2fc195eSAndrew Gallatin {
3920b2fc195eSAndrew Gallatin 	return EINVAL;
3921b2fc195eSAndrew Gallatin }
3922b2fc195eSAndrew Gallatin 
3923b2fc195eSAndrew Gallatin static int
mxge_change_mtu(mxge_softc_t * sc,int mtu)39246d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3925b2fc195eSAndrew Gallatin {
392693037a67SJustin Hibbits 	if_t ifp = sc->ifp;
3927b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3928b2fc195eSAndrew Gallatin 	int err = 0;
3929b2fc195eSAndrew Gallatin 
3930c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3931053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3932b2fc195eSAndrew Gallatin 		return EINVAL;
3933a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
393493037a67SJustin Hibbits 	old_mtu = if_getmtu(ifp);
393593037a67SJustin Hibbits 	if_setmtu(ifp, mtu);
393693037a67SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3937a393336bSAndrew Gallatin 		mxge_close(sc, 0);
39386d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3939b2fc195eSAndrew Gallatin 		if (err != 0) {
394093037a67SJustin Hibbits 			if_setmtu(ifp, old_mtu);
3941a393336bSAndrew Gallatin 			mxge_close(sc, 0);
39426d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3943b2fc195eSAndrew Gallatin 		}
3944b2fc195eSAndrew Gallatin 	}
3945a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3946b2fc195eSAndrew Gallatin 	return err;
3947b2fc195eSAndrew Gallatin }
3948b2fc195eSAndrew Gallatin 
3949b2fc195eSAndrew Gallatin static void
mxge_media_status(if_t ifp,struct ifmediareq * ifmr)395093037a67SJustin Hibbits mxge_media_status(if_t ifp, struct ifmediareq *ifmr)
3951b2fc195eSAndrew Gallatin {
395293037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
3953b2fc195eSAndrew Gallatin 
3954b2fc195eSAndrew Gallatin 	if (sc == NULL)
3955b2fc195eSAndrew Gallatin 		return;
3956b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3957c406ad2eSAndrew Gallatin 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
3958c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3959c406ad2eSAndrew Gallatin 	ifmr->ifm_active |= sc->current_media;
3960b2fc195eSAndrew Gallatin }
3961b2fc195eSAndrew Gallatin 
3962b2fc195eSAndrew Gallatin static int
mxge_fetch_i2c(mxge_softc_t * sc,struct ifi2creq * i2c)3963df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c)
3964df131e84SAndrew Gallatin {
3965df131e84SAndrew Gallatin 	mxge_cmd_t cmd;
3966df131e84SAndrew Gallatin 	uint32_t i2c_args;
3967df131e84SAndrew Gallatin 	int i, ms, err;
3968df131e84SAndrew Gallatin 
3969df131e84SAndrew Gallatin 	if (i2c->dev_addr != 0xA0 &&
3970df131e84SAndrew Gallatin 	    i2c->dev_addr != 0xA2)
3971df131e84SAndrew Gallatin 		return (EINVAL);
3972df131e84SAndrew Gallatin 	if (i2c->len > sizeof(i2c->data))
3973df131e84SAndrew Gallatin 		return (EINVAL);
3974df131e84SAndrew Gallatin 
3975df131e84SAndrew Gallatin 	for (i = 0; i < i2c->len; i++) {
3976df131e84SAndrew Gallatin 		i2c_args = i2c->dev_addr << 0x8;
3977df131e84SAndrew Gallatin 		i2c_args |= i2c->offset + i;
3978df131e84SAndrew Gallatin 		cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
3979df131e84SAndrew Gallatin 		cmd.data1 = i2c_args;
3980df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
3981df131e84SAndrew Gallatin 
3982df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
3983df131e84SAndrew Gallatin 			return (EIO);
3984df131e84SAndrew Gallatin 		/* now we wait for the data to be cached */
3985df131e84SAndrew Gallatin 		cmd.data0 = i2c_args & 0xff;
3986df131e84SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3987df131e84SAndrew Gallatin 		for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
3988df131e84SAndrew Gallatin 			cmd.data0 = i2c_args & 0xff;
3989df131e84SAndrew Gallatin 			err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
3990df131e84SAndrew Gallatin 			if (err == EBUSY)
3991df131e84SAndrew Gallatin 				DELAY(1000);
3992df131e84SAndrew Gallatin 		}
3993df131e84SAndrew Gallatin 		if (err != MXGEFW_CMD_OK)
3994df131e84SAndrew Gallatin 			return (EIO);
3995df131e84SAndrew Gallatin 		i2c->data[i] = cmd.data0;
3996df131e84SAndrew Gallatin 	}
3997df131e84SAndrew Gallatin 	return (0);
3998df131e84SAndrew Gallatin }
3999df131e84SAndrew Gallatin 
4000df131e84SAndrew Gallatin static int
mxge_ioctl(if_t ifp,u_long command,caddr_t data)400193037a67SJustin Hibbits mxge_ioctl(if_t ifp, u_long command, caddr_t data)
4002b2fc195eSAndrew Gallatin {
400393037a67SJustin Hibbits 	mxge_softc_t *sc = if_getsoftc(ifp);
4004b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
4005df131e84SAndrew Gallatin 	struct ifi2creq i2c;
4006b2fc195eSAndrew Gallatin 	int err, mask;
4007b2fc195eSAndrew Gallatin 
4008b2fc195eSAndrew Gallatin 	err = 0;
4009b2fc195eSAndrew Gallatin 	switch (command) {
4010b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
40116d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
4012b2fc195eSAndrew Gallatin 		break;
4013b2fc195eSAndrew Gallatin 
4014b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
4015a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
40168c5d766cSAndrew Gallatin 		if (sc->dying) {
40178c5d766cSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
40188c5d766cSAndrew Gallatin 			return EINVAL;
40198c5d766cSAndrew Gallatin 		}
402093037a67SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
402193037a67SJustin Hibbits 			if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
40226d87a65dSAndrew Gallatin 				err = mxge_open(sc);
4023dce01b9bSAndrew Gallatin 			} else {
40240fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
40250fa7f681SAndrew Gallatin 				   flag chages */
40260fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
402793037a67SJustin Hibbits 						    if_getflags(ifp) & IFF_PROMISC);
40280fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
40290fa7f681SAndrew Gallatin 			}
4030b2fc195eSAndrew Gallatin 		} else {
403193037a67SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
4032a393336bSAndrew Gallatin 				mxge_close(sc, 0);
4033dce01b9bSAndrew Gallatin 			}
4034b2fc195eSAndrew Gallatin 		}
4035a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4036b2fc195eSAndrew Gallatin 		break;
4037b2fc195eSAndrew Gallatin 
4038b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
4039b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
4040a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4041c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4042c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4043c0a1f0afSAndrew Gallatin 			return (EINVAL);
4044c0a1f0afSAndrew Gallatin 		}
40450fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
4046a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4047b2fc195eSAndrew Gallatin 		break;
4048b2fc195eSAndrew Gallatin 
4049b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
4050a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
405193037a67SJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
4052b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
405393037a67SJustin Hibbits 			if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
4054cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO4;
405593037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, (IFCAP_TXCSUM|IFCAP_TSO4));
405693037a67SJustin Hibbits 				if_sethwassistbits(ifp, 0, (CSUM_TCP | CSUM_UDP));
4057b2fc195eSAndrew Gallatin 			} else {
405893037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TXCSUM, 0);
405993037a67SJustin Hibbits 				if_sethwassistbits(ifp, (CSUM_TCP | CSUM_UDP), 0);
4060b2fc195eSAndrew Gallatin 			}
4061cbb9ccf7SRyan Moeller 		}
4062cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM) {
406393037a67SJustin Hibbits 			if (IFCAP_RXCSUM & if_getcapenable(ifp)) {
406493037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_RXCSUM);
4065b2fc195eSAndrew Gallatin 			} else {
406693037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_RXCSUM, 0);
4067b2fc195eSAndrew Gallatin 			}
4068b2fc195eSAndrew Gallatin 		}
4069aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
407093037a67SJustin Hibbits 			if (IFCAP_TSO4 & if_getcapenable(ifp)) {
407193037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_TSO4);
407293037a67SJustin Hibbits 			} else if (IFCAP_TXCSUM & if_getcapenable(ifp)) {
407393037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TSO4, 0);
407493037a67SJustin Hibbits 				if_sethwassistbits(ifp, CSUM_TSO, 0);
4075aed8e389SAndrew Gallatin 			} else {
4076aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
4077aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
4078aed8e389SAndrew Gallatin 				err = EINVAL;
4079aed8e389SAndrew Gallatin 			}
4080aed8e389SAndrew Gallatin 		}
40810a7a780eSAndrew Gallatin #if IFCAP_TSO6
40820a7a780eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM_IPV6) {
408393037a67SJustin Hibbits 			if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
4084cbb9ccf7SRyan Moeller 				mask &= ~IFCAP_TSO6;
408593037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0,
408693037a67SJustin Hibbits 				    IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
408793037a67SJustin Hibbits 				if_sethwassistbits(ifp, 0,
408893037a67SJustin Hibbits 				    CSUM_TCP_IPV6 | CSUM_UDP);
40890a7a780eSAndrew Gallatin 			} else {
409093037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TXCSUM_IPV6, 0);
409193037a67SJustin Hibbits 				if_sethwassistbits(ifp,
409293037a67SJustin Hibbits 				    CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
40930a7a780eSAndrew Gallatin 			}
4094cbb9ccf7SRyan Moeller 		}
4095cbb9ccf7SRyan Moeller 		if (mask & IFCAP_RXCSUM_IPV6) {
409693037a67SJustin Hibbits 			if (IFCAP_RXCSUM_IPV6 & if_getcapenable(ifp)) {
409793037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_RXCSUM_IPV6);
40980a7a780eSAndrew Gallatin 			} else {
409993037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_RXCSUM_IPV6, 0);
41000a7a780eSAndrew Gallatin 			}
41010a7a780eSAndrew Gallatin 		}
41020a7a780eSAndrew Gallatin 		if (mask & IFCAP_TSO6) {
410393037a67SJustin Hibbits 			if (IFCAP_TSO6 & if_getcapenable(ifp)) {
410493037a67SJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_TSO6);
410593037a67SJustin Hibbits 			} else if (IFCAP_TXCSUM_IPV6 & if_getcapenable(ifp)) {
410693037a67SJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TSO6, 0);
410793037a67SJustin Hibbits 				if_sethwassistbits(ifp, CSUM_TSO, 0);
41080a7a780eSAndrew Gallatin 			} else {
41090a7a780eSAndrew Gallatin 				printf("mxge requires tx checksum offload"
41100a7a780eSAndrew Gallatin 				       " be enabled to use TSO\n");
41110a7a780eSAndrew Gallatin 				err = EINVAL;
41120a7a780eSAndrew Gallatin 			}
41130a7a780eSAndrew Gallatin 		}
41140a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */
41150a7a780eSAndrew Gallatin 
411626dd49c6SAndrew Gallatin 		if (mask & IFCAP_LRO)
411793037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
4118c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
411993037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
41200dce6781SAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTSO)
412193037a67SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
41220dce6781SAndrew Gallatin 
412393037a67SJustin Hibbits 		if (!(if_getcapabilities(ifp) & IFCAP_VLAN_HWTSO) ||
412493037a67SJustin Hibbits 		    !(if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING))
412593037a67SJustin Hibbits 			if_setcapenablebit(ifp, 0, IFCAP_VLAN_HWTSO);
41260dce6781SAndrew Gallatin 
4127a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4128c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
4129c792928fSAndrew Gallatin 
4130b2fc195eSAndrew Gallatin 		break;
4131b2fc195eSAndrew Gallatin 
4132b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
4133c406ad2eSAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4134c0a1f0afSAndrew Gallatin 		if (sc->dying) {
4135c0a1f0afSAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4136c0a1f0afSAndrew Gallatin 			return (EINVAL);
4137c0a1f0afSAndrew Gallatin 		}
4138c406ad2eSAndrew Gallatin 		mxge_media_probe(sc);
4139c406ad2eSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4140b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
4141b2fc195eSAndrew Gallatin 				    &sc->media, command);
4142b2fc195eSAndrew Gallatin 		break;
4143b2fc195eSAndrew Gallatin 
4144df131e84SAndrew Gallatin 	case SIOCGI2C:
4145df131e84SAndrew Gallatin 		if (sc->connector != MXGE_XFP &&
4146df131e84SAndrew Gallatin 		    sc->connector != MXGE_SFP) {
4147df131e84SAndrew Gallatin 			err = ENXIO;
4148df131e84SAndrew Gallatin 			break;
4149df131e84SAndrew Gallatin 		}
4150df131e84SAndrew Gallatin 		err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c));
4151df131e84SAndrew Gallatin 		if (err != 0)
4152df131e84SAndrew Gallatin 			break;
4153df131e84SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
4154df131e84SAndrew Gallatin 		if (sc->dying) {
4155df131e84SAndrew Gallatin 			mtx_unlock(&sc->driver_mtx);
4156df131e84SAndrew Gallatin 			return (EINVAL);
4157df131e84SAndrew Gallatin 		}
4158df131e84SAndrew Gallatin 		err = mxge_fetch_i2c(sc, &i2c);
4159df131e84SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
4160df131e84SAndrew Gallatin 		if (err == 0)
41611b8b041cSBrooks Davis 			err = copyout(&i2c, ifr_data_get_ptr(ifr),
4162df131e84SAndrew Gallatin 			    sizeof(i2c));
4163df131e84SAndrew Gallatin 		break;
4164b2fc195eSAndrew Gallatin 	default:
4165c756fb6eSRavi Pokala 		err = ether_ioctl(ifp, command, data);
4166c756fb6eSRavi Pokala 		break;
4167b2fc195eSAndrew Gallatin 	}
4168b2fc195eSAndrew Gallatin 	return err;
4169b2fc195eSAndrew Gallatin }
4170b2fc195eSAndrew Gallatin 
4171b2fc195eSAndrew Gallatin static void
mxge_fetch_tunables(mxge_softc_t * sc)41726d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
4173b2fc195eSAndrew Gallatin {
4174b2fc195eSAndrew Gallatin 
41751e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
41766d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
41776d87a65dSAndrew Gallatin 			  &mxge_flow_control);
41786d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
41796d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
41806d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
41816d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
4182d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
4183d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
41845e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
41855e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
41865e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
41875e7d8541SAndrew Gallatin 			  &mxge_verbose);
4188dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
41891e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
41901e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
419194c7d993SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type);
4192f9453025SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
419365c69066SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle);
4194b2fc195eSAndrew Gallatin 
41955e7d8541SAndrew Gallatin 	if (bootverbose)
41965e7d8541SAndrew Gallatin 		mxge_verbose = 1;
41976d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
41986d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
4199dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
42001e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
42016d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
42021e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
4203bb8ddc66SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
42045769c5efSAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
4205b2fc195eSAndrew Gallatin 	}
4206f9453025SAndrew Gallatin 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
4207f9453025SAndrew Gallatin 	    mxge_initial_mtu < ETHER_MIN_LEN)
4208f9453025SAndrew Gallatin 		mxge_initial_mtu = ETHERMTU_JUMBO;
420965c69066SAndrew Gallatin 
421065c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE)
421165c69066SAndrew Gallatin 		mxge_throttle = MXGE_MAX_THROTTLE;
421265c69066SAndrew Gallatin 	if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE)
421365c69066SAndrew Gallatin 		mxge_throttle = MXGE_MIN_THROTTLE;
421465c69066SAndrew Gallatin 	sc->throttle = mxge_throttle;
42151e413cf9SAndrew Gallatin }
42161e413cf9SAndrew Gallatin 
42171e413cf9SAndrew Gallatin static void
mxge_free_slices(mxge_softc_t * sc)42181e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
42191e413cf9SAndrew Gallatin {
42201e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42211e413cf9SAndrew Gallatin 	int i;
42221e413cf9SAndrew Gallatin 
42231e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42241e413cf9SAndrew Gallatin 		return;
42251e413cf9SAndrew Gallatin 
42261e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42271e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42281e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
42291e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
42301e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
4231c6cb3e3fSAndrew Gallatin 			if (ss->tx.br != NULL) {
4232c6cb3e3fSAndrew Gallatin 				drbr_free(ss->tx.br, M_DEVBUF);
4233c6cb3e3fSAndrew Gallatin 				ss->tx.br = NULL;
4234c6cb3e3fSAndrew Gallatin 			}
42351e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
42361e413cf9SAndrew Gallatin 		}
42371e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
42381e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
42391e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
42401e413cf9SAndrew Gallatin 		}
42411e413cf9SAndrew Gallatin 	}
42421e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
42431e413cf9SAndrew Gallatin 	sc->ss = NULL;
42441e413cf9SAndrew Gallatin }
42451e413cf9SAndrew Gallatin 
42461e413cf9SAndrew Gallatin static int
mxge_alloc_slices(mxge_softc_t * sc)42471e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
42481e413cf9SAndrew Gallatin {
42491e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
42501e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
42511e413cf9SAndrew Gallatin 	size_t bytes;
42521e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
42531e413cf9SAndrew Gallatin 
42541e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
42551e413cf9SAndrew Gallatin 	if (err != 0) {
42561e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
42571e413cf9SAndrew Gallatin 		return err;
42581e413cf9SAndrew Gallatin 	}
42591e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
42601e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
42611e413cf9SAndrew Gallatin 
4262ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->ss) * sc->num_slices;
4263ac2fffa4SPedro F. Giffuni 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
42641e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
42651e413cf9SAndrew Gallatin 		return (ENOMEM);
42661e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
42671e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
42681e413cf9SAndrew Gallatin 
42691e413cf9SAndrew Gallatin 		ss->sc = sc;
42701e413cf9SAndrew Gallatin 
42711e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
42721e413cf9SAndrew Gallatin 
42731e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
42741e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
42751e413cf9SAndrew Gallatin 		if (err != 0)
42761e413cf9SAndrew Gallatin 			goto abort;
42771e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
42781e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
42791e413cf9SAndrew Gallatin 
42801e413cf9SAndrew Gallatin 		/*
42811e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
42821e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
42831e413cf9SAndrew Gallatin 		 * slice for now
42841e413cf9SAndrew Gallatin 		 */
42851e413cf9SAndrew Gallatin 
42861e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
42871e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
42881e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
42891e413cf9SAndrew Gallatin 		if (err != 0)
42901e413cf9SAndrew Gallatin 			goto abort;
42911e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
42921e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
42931e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
42941e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
4295c6cb3e3fSAndrew Gallatin 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4296c6cb3e3fSAndrew Gallatin 					   &ss->tx.mtx);
42971e413cf9SAndrew Gallatin 	}
42981e413cf9SAndrew Gallatin 
42991e413cf9SAndrew Gallatin 	return (0);
43001e413cf9SAndrew Gallatin 
43011e413cf9SAndrew Gallatin abort:
43021e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
43031e413cf9SAndrew Gallatin 	return (ENOMEM);
43041e413cf9SAndrew Gallatin }
43051e413cf9SAndrew Gallatin 
43061e413cf9SAndrew Gallatin static void
mxge_slice_probe(mxge_softc_t * sc)43071e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
43081e413cf9SAndrew Gallatin {
43091e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
43101e413cf9SAndrew Gallatin 	char *old_fw;
43111e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
43121e413cf9SAndrew Gallatin 
43131e413cf9SAndrew Gallatin 	sc->num_slices = 1;
43141e413cf9SAndrew Gallatin 	/*
43151e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
43161e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
43171e413cf9SAndrew Gallatin 	 */
43181e413cf9SAndrew Gallatin 
43191e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
43201e413cf9SAndrew Gallatin 		return;
43211e413cf9SAndrew Gallatin 
43221e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
43231e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
43241e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
43251e413cf9SAndrew Gallatin 		return;
43261e413cf9SAndrew Gallatin 
43271e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
43281e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
43291e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
43301e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
43311e413cf9SAndrew Gallatin 	else
43321e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
43331e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
43341e413cf9SAndrew Gallatin 	if (status != 0) {
43351e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
43361e413cf9SAndrew Gallatin 		return;
43371e413cf9SAndrew Gallatin 	}
43381e413cf9SAndrew Gallatin 
43391e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
43401e413cf9SAndrew Gallatin 	   is alive */
43411e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
43421e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
43431e413cf9SAndrew Gallatin 	if (status != 0) {
43441e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
43451e413cf9SAndrew Gallatin 		goto abort_with_fw;
43461e413cf9SAndrew Gallatin 	}
43471e413cf9SAndrew Gallatin 
43481e413cf9SAndrew Gallatin 	/* get rx ring size */
43491e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
43501e413cf9SAndrew Gallatin 	if (status != 0) {
43511e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
43521e413cf9SAndrew Gallatin 		goto abort_with_fw;
43531e413cf9SAndrew Gallatin 	}
43541e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
43551e413cf9SAndrew Gallatin 
43561e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
43571e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
43581e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
43591e413cf9SAndrew Gallatin 	if (status != 0) {
43601e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
43611e413cf9SAndrew Gallatin 		goto abort_with_fw;
43621e413cf9SAndrew Gallatin 	}
43631e413cf9SAndrew Gallatin 
43641e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
43651e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
43661e413cf9SAndrew Gallatin 	if (status != 0) {
43671e413cf9SAndrew Gallatin 		device_printf(sc->dev,
43681e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
43691e413cf9SAndrew Gallatin 		goto abort_with_fw;
43701e413cf9SAndrew Gallatin 	}
43711e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
43721e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
43731e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
43741e413cf9SAndrew Gallatin 
43751e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
43761e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
43771e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
43781e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
43791e413cf9SAndrew Gallatin 	} else {
43801e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
43811e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
43821e413cf9SAndrew Gallatin 	}
43831e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
43841e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
43851e413cf9SAndrew Gallatin 		sc->num_slices--;
43861e413cf9SAndrew Gallatin 
43871e413cf9SAndrew Gallatin 	if (mxge_verbose)
43881e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
43891e413cf9SAndrew Gallatin 			      sc->num_slices);
43901e413cf9SAndrew Gallatin 
43911e413cf9SAndrew Gallatin 	return;
43921e413cf9SAndrew Gallatin 
43931e413cf9SAndrew Gallatin abort_with_fw:
43941e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
43951e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
43961e413cf9SAndrew Gallatin }
43971e413cf9SAndrew Gallatin 
43981e413cf9SAndrew Gallatin static int
mxge_add_msix_irqs(mxge_softc_t * sc)43991e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
44001e413cf9SAndrew Gallatin {
4401ac2fffa4SPedro F. Giffuni 	size_t bytes;
44021e413cf9SAndrew Gallatin 	int count, err, i, rid;
44031e413cf9SAndrew Gallatin 
44041e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
44051e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
44061e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
44071e413cf9SAndrew Gallatin 
44081e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
44091e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
44101e413cf9SAndrew Gallatin 		return ENXIO;
44111e413cf9SAndrew Gallatin 	}
44121e413cf9SAndrew Gallatin 
44131e413cf9SAndrew Gallatin 	count = sc->num_slices;
44141e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
44151e413cf9SAndrew Gallatin 	if (err != 0) {
44161e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
44171e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
44181e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
44191e413cf9SAndrew Gallatin 	}
44201e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
44211e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
44221e413cf9SAndrew Gallatin 			      count, sc->num_slices);
44231e413cf9SAndrew Gallatin 		device_printf(sc->dev,
44241e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
44251e413cf9SAndrew Gallatin 			      count);
44261e413cf9SAndrew Gallatin 		err = ENOSPC;
44271e413cf9SAndrew Gallatin 		goto abort_with_msix;
44281e413cf9SAndrew Gallatin 	}
4429ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4430ac2fffa4SPedro F. Giffuni 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44311e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
44321e413cf9SAndrew Gallatin 		err = ENOMEM;
44331e413cf9SAndrew Gallatin 		goto abort_with_msix;
44341e413cf9SAndrew Gallatin 	}
44351e413cf9SAndrew Gallatin 
44361e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44371e413cf9SAndrew Gallatin 		rid = i + 1;
44381e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
44391e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
44401e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
44411e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
44421e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
44431e413cf9SAndrew Gallatin 				      " for message %d\n", i);
44441e413cf9SAndrew Gallatin 			err = ENXIO;
44451e413cf9SAndrew Gallatin 			goto abort_with_res;
44461e413cf9SAndrew Gallatin 		}
44471e413cf9SAndrew Gallatin 	}
44481e413cf9SAndrew Gallatin 
4449ac2fffa4SPedro F. Giffuni 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4450ac2fffa4SPedro F. Giffuni 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
44511e413cf9SAndrew Gallatin 
44521e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44531e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
4454c98b837aSElliott Mitchell 				     INTR_TYPE_NET | INTR_MPSAFE, NULL,
445537d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
44561e413cf9SAndrew Gallatin 		if (err != 0) {
44571e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
44581e413cf9SAndrew Gallatin 				      "message %d\n", i);
44591e413cf9SAndrew Gallatin 			goto abort_with_intr;
44601e413cf9SAndrew Gallatin 		}
446121089137SAndrew Gallatin 		bus_describe_intr(sc->dev, sc->msix_irq_res[i],
446221089137SAndrew Gallatin 				  sc->msix_ih[i], "s%d", i);
44631e413cf9SAndrew Gallatin 	}
44641e413cf9SAndrew Gallatin 
44651e413cf9SAndrew Gallatin 	if (mxge_verbose) {
44661e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
44671e413cf9SAndrew Gallatin 			      sc->num_slices);
44681e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
4469da1b038aSJustin Hibbits 			printf(" %jd", rman_get_start(sc->msix_irq_res[i]));
44701e413cf9SAndrew Gallatin 		printf("\n");
44711e413cf9SAndrew Gallatin 	}
44721e413cf9SAndrew Gallatin 	return (0);
44731e413cf9SAndrew Gallatin 
44741e413cf9SAndrew Gallatin abort_with_intr:
44751e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44761e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
44771e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
44781e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
44791e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
44801e413cf9SAndrew Gallatin 		}
44811e413cf9SAndrew Gallatin 	}
44821e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
44831e413cf9SAndrew Gallatin 
44841e413cf9SAndrew Gallatin abort_with_res:
44851e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
44861e413cf9SAndrew Gallatin 		rid = i + 1;
44871e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
44881e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44891e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
44901e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
44911e413cf9SAndrew Gallatin 	}
44921e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
44931e413cf9SAndrew Gallatin 
44941e413cf9SAndrew Gallatin abort_with_msix:
44951e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
44961e413cf9SAndrew Gallatin 
44971e413cf9SAndrew Gallatin abort_with_msix_table:
44981e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
44991e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45001e413cf9SAndrew Gallatin 
45011e413cf9SAndrew Gallatin 	return err;
45021e413cf9SAndrew Gallatin }
45031e413cf9SAndrew Gallatin 
45041e413cf9SAndrew Gallatin static int
mxge_add_single_irq(mxge_softc_t * sc)45051e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
45061e413cf9SAndrew Gallatin {
45071e413cf9SAndrew Gallatin 	int count, err, rid;
45081e413cf9SAndrew Gallatin 
45091e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
45101e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
45111e413cf9SAndrew Gallatin 		rid = 1;
45121e413cf9SAndrew Gallatin 	} else {
45131e413cf9SAndrew Gallatin 		rid = 0;
451491ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
45151e413cf9SAndrew Gallatin 	}
451643cd6160SJustin Hibbits 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid,
451743cd6160SJustin Hibbits 					     RF_SHAREABLE | RF_ACTIVE);
45181e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
45191e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
45201e413cf9SAndrew Gallatin 		return ENXIO;
45211e413cf9SAndrew Gallatin 	}
45221e413cf9SAndrew Gallatin 	if (mxge_verbose)
4523da1b038aSJustin Hibbits 		device_printf(sc->dev, "using %s irq %jd\n",
452491ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
45251e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
45261e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
4527c98b837aSElliott Mitchell 			     INTR_TYPE_NET | INTR_MPSAFE, NULL,
452837d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
45291e413cf9SAndrew Gallatin 	if (err != 0) {
45301e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
453191ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
453291ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
45331e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
45341e413cf9SAndrew Gallatin 	}
45351e413cf9SAndrew Gallatin 	return err;
45361e413cf9SAndrew Gallatin }
45371e413cf9SAndrew Gallatin 
45381e413cf9SAndrew Gallatin static void
mxge_rem_msix_irqs(mxge_softc_t * sc)45391e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
45401e413cf9SAndrew Gallatin {
45411e413cf9SAndrew Gallatin 	int i, rid;
45421e413cf9SAndrew Gallatin 
45431e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45441e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
45451e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
45461e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
45471e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
45481e413cf9SAndrew Gallatin 		}
45491e413cf9SAndrew Gallatin 	}
45501e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
45511e413cf9SAndrew Gallatin 
45521e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
45531e413cf9SAndrew Gallatin 		rid = i + 1;
45541e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
45551e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
45561e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
45571e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
45581e413cf9SAndrew Gallatin 	}
45591e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
45601e413cf9SAndrew Gallatin 
45611e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
45621e413cf9SAndrew Gallatin 			     sc->msix_table_res);
45631e413cf9SAndrew Gallatin 
45641e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
45651e413cf9SAndrew Gallatin 	return;
45661e413cf9SAndrew Gallatin }
45671e413cf9SAndrew Gallatin 
45681e413cf9SAndrew Gallatin static void
mxge_rem_single_irq(mxge_softc_t * sc)45691e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
45701e413cf9SAndrew Gallatin {
45711e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
45721e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
457391ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
457491ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
45751e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
45761e413cf9SAndrew Gallatin }
45771e413cf9SAndrew Gallatin 
45781e413cf9SAndrew Gallatin static void
mxge_rem_irq(mxge_softc_t * sc)45791e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
45801e413cf9SAndrew Gallatin {
45811e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45821e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
45831e413cf9SAndrew Gallatin 	else
45841e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
45851e413cf9SAndrew Gallatin }
45861e413cf9SAndrew Gallatin 
45871e413cf9SAndrew Gallatin static int
mxge_add_irq(mxge_softc_t * sc)45881e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
45891e413cf9SAndrew Gallatin {
45901e413cf9SAndrew Gallatin 	int err;
45911e413cf9SAndrew Gallatin 
45921e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
45931e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
45941e413cf9SAndrew Gallatin 	else
45951e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
45961e413cf9SAndrew Gallatin 
45971e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
45981e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
45991e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
46001e413cf9SAndrew Gallatin 	}
46011e413cf9SAndrew Gallatin 	return err;
46021e413cf9SAndrew Gallatin }
46031e413cf9SAndrew Gallatin 
4604b2fc195eSAndrew Gallatin static int
mxge_attach(device_t dev)46056d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4606b2fc195eSAndrew Gallatin {
46070a7a780eSAndrew Gallatin 	mxge_cmd_t cmd;
46086d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
460993037a67SJustin Hibbits 	if_t ifp;
46101e413cf9SAndrew Gallatin 	int err, rid;
4611b2fc195eSAndrew Gallatin 
4612b2fc195eSAndrew Gallatin 	sc->dev = dev;
46136d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4614b2fc195eSAndrew Gallatin 
461572c042dfSAndrew Gallatin 	TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc);
4616a4a75d67SJohn Baldwin 	sc->tq = taskqueue_create("mxge_taskq", M_WAITOK,
4617a4a75d67SJohn Baldwin 				  taskqueue_thread_enqueue, &sc->tq);
461872c042dfSAndrew Gallatin 
461962ce43ccSScott Long 	err = bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
4620b2fc195eSAndrew Gallatin 				 1,			/* alignment */
46211e413cf9SAndrew Gallatin 				 0,			/* boundary */
4622b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4623b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4624b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4625aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
46265e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
46271e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4628b2fc195eSAndrew Gallatin 				 0,			/* flags */
4629b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4630b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4631b2fc195eSAndrew Gallatin 
4632b2fc195eSAndrew Gallatin 	if (err != 0) {
4633b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4634b2fc195eSAndrew Gallatin 			      err);
463572c042dfSAndrew Gallatin 		goto abort_with_tq;
4636b2fc195eSAndrew Gallatin 	}
4637b2fc195eSAndrew Gallatin 
4638b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
46391e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
46401e413cf9SAndrew Gallatin 
4641a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4642a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4643a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4644a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4645a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4646a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4647b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4648b2fc195eSAndrew Gallatin 
4649dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4650d91b1b49SAndrew Gallatin 
4651dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4652b2fc195eSAndrew Gallatin 
4653b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4654b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
465543cd6160SJustin Hibbits 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
465643cd6160SJustin Hibbits 					     RF_ACTIVE);
4657b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4658b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4659b2fc195eSAndrew Gallatin 		err = ENXIO;
4660b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4661b2fc195eSAndrew Gallatin 	}
4662b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4663b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4664b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4665da1b038aSJustin Hibbits 		device_printf(dev, "impossible memory region size %jd\n",
4666b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4667b2fc195eSAndrew Gallatin 		err = ENXIO;
4668b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4669b2fc195eSAndrew Gallatin 	}
4670b2fc195eSAndrew Gallatin 
4671b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4672b2fc195eSAndrew Gallatin 	   lanai SRAM */
46736d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4674b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4675b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
46766d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4677b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
46786d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
46796d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4680b2fc195eSAndrew Gallatin 	if (err != 0)
4681b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4682b2fc195eSAndrew Gallatin 
4683b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
46846d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4685b2fc195eSAndrew Gallatin 
4686b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
46876d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
46886d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4689b2fc195eSAndrew Gallatin 	if (err != 0)
4690b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4691b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
46926d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4693b2fc195eSAndrew Gallatin 	if (err != 0)
4694b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4695b2fc195eSAndrew Gallatin 
4696a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4697a98d6cd7SAndrew Gallatin 	if (err != 0)
46981e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4699b2fc195eSAndrew Gallatin 
47008fe615baSAndrew Gallatin 	/* select & load the firmware */
47018fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4702b2fc195eSAndrew Gallatin 	if (err != 0)
47031e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47045e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
47051e413cf9SAndrew Gallatin 
47061e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
47071e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
47081e413cf9SAndrew Gallatin 	if (err != 0)
47091e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
47101e413cf9SAndrew Gallatin 
4711adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4712b2fc195eSAndrew Gallatin 	if (err != 0)
47131e413cf9SAndrew Gallatin 		goto abort_with_slices;
4714b2fc195eSAndrew Gallatin 
4715a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4716a98d6cd7SAndrew Gallatin 	if (err != 0) {
4717a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
47182e084798SAndrew Gallatin 		goto abort_with_slices;
4719a98d6cd7SAndrew Gallatin 	}
4720a98d6cd7SAndrew Gallatin 
47211e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4722a98d6cd7SAndrew Gallatin 	if (err != 0) {
47231e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4724a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4725a98d6cd7SAndrew Gallatin 	}
47261e413cf9SAndrew Gallatin 
472793037a67SJustin Hibbits 	if_setbaudrate(ifp, IF_Gbps(10));
472893037a67SJustin Hibbits 	if_setcapabilities(ifp, IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
472926dd49c6SAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 |
473093037a67SJustin Hibbits 		IFCAP_RXCSUM_IPV6);
473126dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6)
473293037a67SJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_LRO, 0);
4733eb6219e3SAndrew Gallatin #endif
473437d89b0cSAndrew Gallatin 
473537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
473693037a67SJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM, 0);
47370dce6781SAndrew Gallatin 
47380dce6781SAndrew Gallatin 	/* Only FW 1.4.32 and newer can do TSO over vlans */
47390dce6781SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
47400dce6781SAndrew Gallatin 	    sc->fw_ver_tiny >= 32)
474193037a67SJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTSO, 0);
474237d89b0cSAndrew Gallatin #endif
4743053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4744053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
474593037a67SJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_JUMBO_MTU, 0);
4746053e637fSAndrew Gallatin 	else
4747053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4748adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4749053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
475093037a67SJustin Hibbits 	if_sethwassist(ifp, CSUM_TCP | CSUM_UDP | CSUM_TSO);
475193037a67SJustin Hibbits 	if_sethwassistbits(ifp, CSUM_TCP_IPV6 | CSUM_UDP_IPV6, 0);
47520a7a780eSAndrew Gallatin 	/* check to see if f/w supports TSO for IPv6 */
47530a7a780eSAndrew Gallatin 	if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) {
47540a7a780eSAndrew Gallatin 		if (CSUM_TCP_IPV6)
475593037a67SJustin Hibbits 			if_setcapabilitiesbit(ifp, IFCAP_TSO6, 0);
47560a7a780eSAndrew Gallatin 		sc->max_tso6_hlen = min(cmd.data0,
47570a7a780eSAndrew Gallatin 					sizeof (sc->ss[0].scratch));
47580a7a780eSAndrew Gallatin 	}
475993037a67SJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
4760f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
476193037a67SJustin Hibbits 		if_setcapenablebit(ifp, 0, IFCAP_LRO);
476293037a67SJustin Hibbits 	if_setinitfn(ifp, mxge_init);
476393037a67SJustin Hibbits 	if_setsoftc(ifp, sc);
476493037a67SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
476593037a67SJustin Hibbits 	if_setioctlfn(ifp, mxge_ioctl);
476693037a67SJustin Hibbits 	if_setstartfn(ifp, mxge_start);
476793037a67SJustin Hibbits 	if_setgetcounterfn(ifp, mxge_get_counter);
476893037a67SJustin Hibbits 	if_sethwtsomax(ifp, IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
476993037a67SJustin Hibbits 	if_sethwtsomaxsegcount(ifp, sc->ss[0].tx.max_desc);
477093037a67SJustin Hibbits 	if_sethwtsomaxsegsize(ifp, IP_MAXPACKET);
4771c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4772c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4773c587e59fSAndrew Gallatin 		     mxge_media_status);
4774c406ad2eSAndrew Gallatin 	mxge_media_init(sc);
4775c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
47768c5d766cSAndrew Gallatin 	sc->dying = 0;
4777b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4778f9453025SAndrew Gallatin 	/* ether_ifattach sets mtu to ETHERMTU */
4779f9453025SAndrew Gallatin 	if (mxge_initial_mtu != ETHERMTU)
4780f9453025SAndrew Gallatin 		mxge_change_mtu(sc, mxge_initial_mtu);
4781b2fc195eSAndrew Gallatin 
47826d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
478393037a67SJustin Hibbits 	if_settransmitfn(ifp, mxge_transmit);
478493037a67SJustin Hibbits 	if_setqflushfn(ifp, mxge_qflush);
47852e084798SAndrew Gallatin 	taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq",
47862e084798SAndrew Gallatin 				device_get_nameunit(sc->dev));
47876b484a49SAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
4788b2fc195eSAndrew Gallatin 	return 0;
4789b2fc195eSAndrew Gallatin 
4790a98d6cd7SAndrew Gallatin abort_with_rings:
4791a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
47921e413cf9SAndrew Gallatin abort_with_slices:
47931e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4794a98d6cd7SAndrew Gallatin abort_with_dmabench:
4795a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4796b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
47976d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4798b2fc195eSAndrew Gallatin abort_with_cmd_dma:
47996d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4800b2fc195eSAndrew Gallatin abort_with_mem_res:
4801b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4802b2fc195eSAndrew Gallatin abort_with_lock:
4803b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4804a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4805a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4806b2fc195eSAndrew Gallatin 	if_free(ifp);
4807b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
480872c042dfSAndrew Gallatin abort_with_tq:
480972c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
481072c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
481172c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
481272c042dfSAndrew Gallatin 		sc->tq = NULL;
481372c042dfSAndrew Gallatin 	}
4814b2fc195eSAndrew Gallatin 	return err;
4815b2fc195eSAndrew Gallatin }
4816b2fc195eSAndrew Gallatin 
4817b2fc195eSAndrew Gallatin static int
mxge_detach(device_t dev)48186d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4819b2fc195eSAndrew Gallatin {
48206d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4821b2fc195eSAndrew Gallatin 
482237d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4823c792928fSAndrew Gallatin 		device_printf(sc->dev,
4824c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4825c792928fSAndrew Gallatin 		return EBUSY;
4826c792928fSAndrew Gallatin 	}
4827a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
48288c5d766cSAndrew Gallatin 	sc->dying = 1;
482993037a67SJustin Hibbits 	if (if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING)
4830a393336bSAndrew Gallatin 		mxge_close(sc, 0);
4831a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4832b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
483372c042dfSAndrew Gallatin 	if (sc->tq != NULL) {
483472c042dfSAndrew Gallatin 		taskqueue_drain(sc->tq, &sc->watchdog_task);
483572c042dfSAndrew Gallatin 		taskqueue_free(sc->tq);
483672c042dfSAndrew Gallatin 		sc->tq = NULL;
483772c042dfSAndrew Gallatin 	}
4838e749ef6bSAndrew Gallatin 	callout_drain(&sc->co_hdl);
4839dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4840091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
48411e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
48421e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4843a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
48441e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4845a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
48466d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
48476d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4848b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4849b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4850a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4851a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4852b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4853b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4854b2fc195eSAndrew Gallatin 	return 0;
4855b2fc195eSAndrew Gallatin }
4856b2fc195eSAndrew Gallatin 
4857b2fc195eSAndrew Gallatin static int
mxge_shutdown(device_t dev)48586d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4859b2fc195eSAndrew Gallatin {
4860b2fc195eSAndrew Gallatin 	return 0;
4861b2fc195eSAndrew Gallatin }
4862b2fc195eSAndrew Gallatin 
4863b2fc195eSAndrew Gallatin /*
4864b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4865b2fc195eSAndrew Gallatin 
4866b2fc195eSAndrew Gallatin   Local Variables:
4867b2fc195eSAndrew Gallatin   c-file-style:"linux"
4868b2fc195eSAndrew Gallatin   tab-width:8
4869b2fc195eSAndrew Gallatin   End:
4870b2fc195eSAndrew Gallatin */
4871