xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 91ed89132cde0e6d020cb7a510db3b0e1f44330c)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
31e413cf9SAndrew Gallatin Copyright (c) 2006-2008, Myricom Inc.
4b2fc195eSAndrew Gallatin All rights reserved.
5b2fc195eSAndrew Gallatin 
6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without
7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met:
8b2fc195eSAndrew Gallatin 
9b2fc195eSAndrew Gallatin  1. Redistributions of source code must retain the above copyright notice,
10b2fc195eSAndrew Gallatin     this list of conditions and the following disclaimer.
11b2fc195eSAndrew Gallatin 
12eb8e82f5SAndrew Gallatin  2. Neither the name of the Myricom Inc, nor the names of its
13b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
14b2fc195eSAndrew Gallatin     this software without specific prior written permission.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
27b2fc195eSAndrew Gallatin 
28b2fc195eSAndrew Gallatin ***************************************************************************/
29b2fc195eSAndrew Gallatin 
30b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
31b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
32b2fc195eSAndrew Gallatin 
33b2fc195eSAndrew Gallatin #include <sys/param.h>
34b2fc195eSAndrew Gallatin #include <sys/systm.h>
35b2fc195eSAndrew Gallatin #include <sys/linker.h>
36b2fc195eSAndrew Gallatin #include <sys/firmware.h>
37b2fc195eSAndrew Gallatin #include <sys/endian.h>
38b2fc195eSAndrew Gallatin #include <sys/sockio.h>
39b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
40b2fc195eSAndrew Gallatin #include <sys/malloc.h>
41b2fc195eSAndrew Gallatin #include <sys/kdb.h>
42b2fc195eSAndrew Gallatin #include <sys/kernel.h>
434e7f640dSJohn Baldwin #include <sys/lock.h>
44b2fc195eSAndrew Gallatin #include <sys/module.h>
45b2fc195eSAndrew Gallatin #include <sys/memrange.h>
46b2fc195eSAndrew Gallatin #include <sys/socket.h>
47b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
48b2fc195eSAndrew Gallatin #include <sys/sx.h>
49b2fc195eSAndrew Gallatin 
50b2fc195eSAndrew Gallatin #include <net/if.h>
51b2fc195eSAndrew Gallatin #include <net/if_arp.h>
52b2fc195eSAndrew Gallatin #include <net/ethernet.h>
53b2fc195eSAndrew Gallatin #include <net/if_dl.h>
54b2fc195eSAndrew Gallatin #include <net/if_media.h>
55b2fc195eSAndrew Gallatin 
56b2fc195eSAndrew Gallatin #include <net/bpf.h>
57b2fc195eSAndrew Gallatin 
58b2fc195eSAndrew Gallatin #include <net/if_types.h>
59b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
60b2fc195eSAndrew Gallatin #include <net/zlib.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
63b2fc195eSAndrew Gallatin #include <netinet/in.h>
64b2fc195eSAndrew Gallatin #include <netinet/ip.h>
65aed8e389SAndrew Gallatin #include <netinet/tcp.h>
66b2fc195eSAndrew Gallatin 
67b2fc195eSAndrew Gallatin #include <machine/bus.h>
68053e637fSAndrew Gallatin #include <machine/in_cksum.h>
69b2fc195eSAndrew Gallatin #include <machine/resource.h>
70b2fc195eSAndrew Gallatin #include <sys/bus.h>
71b2fc195eSAndrew Gallatin #include <sys/rman.h>
721e413cf9SAndrew Gallatin #include <sys/smp.h>
73b2fc195eSAndrew Gallatin 
74b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
75b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
78b2fc195eSAndrew Gallatin #include <vm/pmap.h>
79b2fc195eSAndrew Gallatin 
80c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64)
81c2c14a69SAndrew Gallatin #include <machine/specialreg.h>
82c2c14a69SAndrew Gallatin #endif
83c2c14a69SAndrew Gallatin 
846d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
856d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
861e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/
876d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
88b2fc195eSAndrew Gallatin 
89b2fc195eSAndrew Gallatin /* tunable params */
906d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
91d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
926d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
935e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
946d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
955e7d8541SAndrew Gallatin static int mxge_verbose = 0;
96f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8;
97dce01b9bSAndrew Gallatin static int mxge_ticks;
981e413cf9SAndrew Gallatin static int mxge_max_slices = 1;
991e413cf9SAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
1001e413cf9SAndrew Gallatin static int mxge_always_promisc = 0;
1016d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1026d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
1031e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1041e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
105b2fc195eSAndrew Gallatin 
1066d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1076d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1086d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1096d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1106d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
111b2fc195eSAndrew Gallatin 
1126d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
113b2fc195eSAndrew Gallatin {
114b2fc195eSAndrew Gallatin   /* Device interface */
1156d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1166d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1176d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1186d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
119b2fc195eSAndrew Gallatin   {0, 0}
120b2fc195eSAndrew Gallatin };
121b2fc195eSAndrew Gallatin 
1226d87a65dSAndrew Gallatin static driver_t mxge_driver =
123b2fc195eSAndrew Gallatin {
1246d87a65dSAndrew Gallatin   "mxge",
1256d87a65dSAndrew Gallatin   mxge_methods,
1266d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
127b2fc195eSAndrew Gallatin };
128b2fc195eSAndrew Gallatin 
1296d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
130b2fc195eSAndrew Gallatin 
131b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1326d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1336d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
134f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1);
135b2fc195eSAndrew Gallatin 
1361e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1378fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
138276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc);
139276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc);
140276edd10SAndrew Gallatin static void mxge_tick(void *arg);
1418fe615baSAndrew Gallatin 
142b2fc195eSAndrew Gallatin static int
1436d87a65dSAndrew Gallatin mxge_probe(device_t dev)
144b2fc195eSAndrew Gallatin {
1456d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
146f1544498SAndrew Gallatin       ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
147f1544498SAndrew Gallatin        (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
148b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
149b2fc195eSAndrew Gallatin 	  return 0;
150b2fc195eSAndrew Gallatin   }
151b2fc195eSAndrew Gallatin   return ENXIO;
152b2fc195eSAndrew Gallatin }
153b2fc195eSAndrew Gallatin 
154b2fc195eSAndrew Gallatin static void
1556d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
156b2fc195eSAndrew Gallatin {
157f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64)
158b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
159b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
160b2fc195eSAndrew Gallatin 	vm_offset_t len;
161b2fc195eSAndrew Gallatin 	int err, action;
162b2fc195eSAndrew Gallatin 
1634d69a9d0SAndrew Gallatin 	sc->wc = 1;
164b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
165c2c14a69SAndrew Gallatin 	err = pmap_change_attr((vm_offset_t) sc->sram,
166c2c14a69SAndrew Gallatin 			       len, PAT_WRITE_COMBINING);
167c2c14a69SAndrew Gallatin 	if (err == 0)
168c2c14a69SAndrew Gallatin 		return;
169c2c14a69SAndrew Gallatin 	else
170c2c14a69SAndrew Gallatin 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
171c2c14a69SAndrew Gallatin 			      err);
172c2c14a69SAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
173b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
174b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
175b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
176b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1776d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
178b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
179b2fc195eSAndrew Gallatin 	if (err != 0) {
1804d69a9d0SAndrew Gallatin 		sc->wc = 0;
181b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
182b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
183b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
184b2fc195eSAndrew Gallatin 	}
185f9ae0280SAndrew Gallatin #endif
186b2fc195eSAndrew Gallatin }
187b2fc195eSAndrew Gallatin 
188b2fc195eSAndrew Gallatin 
189b2fc195eSAndrew Gallatin /* callback to get our DMA address */
190b2fc195eSAndrew Gallatin static void
1916d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
192b2fc195eSAndrew Gallatin 			 int error)
193b2fc195eSAndrew Gallatin {
194b2fc195eSAndrew Gallatin 	if (error == 0) {
195b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
196b2fc195eSAndrew Gallatin 	}
197b2fc195eSAndrew Gallatin }
198b2fc195eSAndrew Gallatin 
199b2fc195eSAndrew Gallatin static int
2006d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
201b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
202b2fc195eSAndrew Gallatin {
203b2fc195eSAndrew Gallatin 	int err;
204b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
2051e413cf9SAndrew Gallatin 	bus_size_t boundary, maxsegsize;
2061e413cf9SAndrew Gallatin 
2071e413cf9SAndrew Gallatin 	if (bytes > 4096 && alignment == 4096) {
2081e413cf9SAndrew Gallatin 		boundary = 0;
2091e413cf9SAndrew Gallatin 		maxsegsize = bytes;
2101e413cf9SAndrew Gallatin 	} else {
2111e413cf9SAndrew Gallatin 		boundary = 4096;
2121e413cf9SAndrew Gallatin 		maxsegsize = 4096;
2131e413cf9SAndrew Gallatin 	}
214b2fc195eSAndrew Gallatin 
215b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
216b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
217b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
2181e413cf9SAndrew Gallatin 				 boundary,		/* boundary */
219b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
220b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
221b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
222b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
223b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2241e413cf9SAndrew Gallatin 				 maxsegsize,		/* maxsegsize */
225b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
226b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
227b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
228b2fc195eSAndrew Gallatin 	if (err != 0) {
229b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
230b2fc195eSAndrew Gallatin 		return err;
231b2fc195eSAndrew Gallatin 	}
232b2fc195eSAndrew Gallatin 
233b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
234b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
235b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
236b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
237b2fc195eSAndrew Gallatin 	if (err != 0) {
238b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
239b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
240b2fc195eSAndrew Gallatin 	}
241b2fc195eSAndrew Gallatin 
242b2fc195eSAndrew Gallatin 	/* load the memory */
243b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2446d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
245b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
246b2fc195eSAndrew Gallatin 	if (err != 0) {
247b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
248b2fc195eSAndrew Gallatin 		goto abort_with_mem;
249b2fc195eSAndrew Gallatin 	}
250b2fc195eSAndrew Gallatin 	return 0;
251b2fc195eSAndrew Gallatin 
252b2fc195eSAndrew Gallatin abort_with_mem:
253b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
254b2fc195eSAndrew Gallatin abort_with_dmat:
255b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
256b2fc195eSAndrew Gallatin 	return err;
257b2fc195eSAndrew Gallatin }
258b2fc195eSAndrew Gallatin 
259b2fc195eSAndrew Gallatin 
260b2fc195eSAndrew Gallatin static void
2616d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
262b2fc195eSAndrew Gallatin {
263b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
264b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
265b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
266b2fc195eSAndrew Gallatin }
267b2fc195eSAndrew Gallatin 
268b2fc195eSAndrew Gallatin /*
269b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
270b2fc195eSAndrew Gallatin  * SN=x\0
271b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
272b2fc195eSAndrew Gallatin  * PC=text\0
273b2fc195eSAndrew Gallatin  */
274b2fc195eSAndrew Gallatin 
275b2fc195eSAndrew Gallatin static int
2766d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
277b2fc195eSAndrew Gallatin {
2786d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
279b2fc195eSAndrew Gallatin 
280b2fc195eSAndrew Gallatin 	char *ptr, *limit;
281b2fc195eSAndrew Gallatin 	int i, found_mac;
282b2fc195eSAndrew Gallatin 
283b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2846d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
285b2fc195eSAndrew Gallatin 	found_mac = 0;
286b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
287b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2885e7d8541SAndrew Gallatin 			ptr += 1;
289b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
290b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2915e7d8541SAndrew Gallatin 				ptr += 3;
292b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
293b2fc195eSAndrew Gallatin 					goto abort;
294b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
295b2fc195eSAndrew Gallatin 				found_mac = 1;
296b2fc195eSAndrew Gallatin 			}
2975e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2985e7d8541SAndrew Gallatin 			ptr += 3;
2995e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
3005e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
3015e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3025e7d8541SAndrew Gallatin 			ptr += 3;
3035e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
3045e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
305b2fc195eSAndrew Gallatin 		}
3066d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
307b2fc195eSAndrew Gallatin 	}
308b2fc195eSAndrew Gallatin 
309b2fc195eSAndrew Gallatin 	if (found_mac)
310b2fc195eSAndrew Gallatin 		return 0;
311b2fc195eSAndrew Gallatin 
312b2fc195eSAndrew Gallatin  abort:
313b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
314b2fc195eSAndrew Gallatin 
315b2fc195eSAndrew Gallatin 	return ENXIO;
316b2fc195eSAndrew Gallatin }
317b2fc195eSAndrew Gallatin 
318b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
3198fe615baSAndrew Gallatin static void
3208fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
321b2fc195eSAndrew Gallatin {
322b2fc195eSAndrew Gallatin 	uint32_t val;
3238fe615baSAndrew Gallatin 	unsigned long base, off;
324b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
3258fe615baSAndrew Gallatin 	device_t pdev, mcp55;
3268fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
327b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
328b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
329b2fc195eSAndrew Gallatin 
3308fe615baSAndrew Gallatin 
3318fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3328fe615baSAndrew Gallatin 		return;
3338fe615baSAndrew Gallatin 
3348fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3358fe615baSAndrew Gallatin 	if (pdev == NULL) {
3368fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3378fe615baSAndrew Gallatin 		return;
3388fe615baSAndrew Gallatin 	}
3398fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3408fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3418fe615baSAndrew Gallatin 
3428fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3438fe615baSAndrew Gallatin 		return;
3448fe615baSAndrew Gallatin 
3458fe615baSAndrew Gallatin 	base = 0;
3468fe615baSAndrew Gallatin 
3478fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3488fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3498fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3508fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3518fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3528fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3538fe615baSAndrew Gallatin 		if (mcp55 &&
3548fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3558fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3568fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3578fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3588fe615baSAndrew Gallatin 		}
3598fe615baSAndrew Gallatin 	}
3608fe615baSAndrew Gallatin 	if (!base)
3618fe615baSAndrew Gallatin 		return;
3628fe615baSAndrew Gallatin 
363b2fc195eSAndrew Gallatin 	/* XXXX
364b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
365b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
366b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
367b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
368b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
369b2fc195eSAndrew Gallatin 	*/
370b2fc195eSAndrew Gallatin #if 0
371b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
372b2fc195eSAndrew Gallatin 	   config space */
373b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
374b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
375b2fc195eSAndrew Gallatin 		val |= 0x40;
376b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3778fe615baSAndrew Gallatin 		return;
378b2fc195eSAndrew Gallatin 	}
379b2fc195eSAndrew Gallatin #endif
380b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
381b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
382b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
383b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
384b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
385b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
386b2fc195eSAndrew Gallatin 	 */
387b2fc195eSAndrew Gallatin 
388b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
389b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
390b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
391b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
392b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
393b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
394b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
395b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
396b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
397b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
398b2fc195eSAndrew Gallatin 
3998fe615baSAndrew Gallatin 	off =  base
400b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
401b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
402b2fc195eSAndrew Gallatin 						 + 8 * slot);
403b2fc195eSAndrew Gallatin 
404b2fc195eSAndrew Gallatin 	/* map it into the kernel */
405b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
406b2fc195eSAndrew Gallatin 
407b2fc195eSAndrew Gallatin 
408b2fc195eSAndrew Gallatin 	if (va == NULL) {
409b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4108fe615baSAndrew Gallatin 		return;
411b2fc195eSAndrew Gallatin 	}
412b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
413b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
414b2fc195eSAndrew Gallatin 
415b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
416b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
417b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
418b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
419b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
420b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
421b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4228fe615baSAndrew Gallatin 		return;
423b2fc195eSAndrew Gallatin 	}
424b2fc195eSAndrew Gallatin 
425b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
426b2fc195eSAndrew Gallatin 	val = *ptr32;
427b2fc195eSAndrew Gallatin 
428b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
429b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
430b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4318fe615baSAndrew Gallatin 		return;
432b2fc195eSAndrew Gallatin 	}
433b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
434b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4355e7d8541SAndrew Gallatin 	if (mxge_verbose)
436b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4375e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4385e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
439b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4408fe615baSAndrew Gallatin 	return;
441b2fc195eSAndrew Gallatin }
442b2fc195eSAndrew Gallatin #else
4438fe615baSAndrew Gallatin static void
444f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
445b2fc195eSAndrew Gallatin {
446b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
447b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4488fe615baSAndrew Gallatin 	return;
449b2fc195eSAndrew Gallatin }
450b2fc195eSAndrew Gallatin #endif
4518fe615baSAndrew Gallatin 
4528fe615baSAndrew Gallatin 
4538fe615baSAndrew Gallatin static int
4548fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4558fe615baSAndrew Gallatin {
4568fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4578fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4588fe615baSAndrew Gallatin 	int status;
4598fe615baSAndrew Gallatin 	uint32_t len;
4608fe615baSAndrew Gallatin 	char *test = " ";
4618fe615baSAndrew Gallatin 
4628fe615baSAndrew Gallatin 
4638fe615baSAndrew Gallatin 	/* Run a small DMA test.
4648fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4658fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4668fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4678fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4688fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4698fe615baSAndrew Gallatin 	 * transfers took to complete.
4708fe615baSAndrew Gallatin 	 */
4718fe615baSAndrew Gallatin 
4721e413cf9SAndrew Gallatin 	len = sc->tx_boundary;
4738fe615baSAndrew Gallatin 
4748fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4758fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4768fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4778fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4788fe615baSAndrew Gallatin 	if (status != 0) {
4798fe615baSAndrew Gallatin 		test = "read";
4808fe615baSAndrew Gallatin 		goto abort;
4818fe615baSAndrew Gallatin 	}
4828fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4838fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
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 * 0x1;
4878fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4888fe615baSAndrew Gallatin 	if (status != 0) {
4898fe615baSAndrew Gallatin 		test = "write";
4908fe615baSAndrew Gallatin 		goto abort;
4918fe615baSAndrew Gallatin 	}
4928fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4938fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4948fe615baSAndrew Gallatin 
4958fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4968fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4978fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4988fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4998fe615baSAndrew Gallatin 	if (status != 0) {
5008fe615baSAndrew Gallatin 		test = "read/write";
5018fe615baSAndrew Gallatin 		goto abort;
5028fe615baSAndrew Gallatin 	}
5038fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5048fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
5058fe615baSAndrew Gallatin 
5068fe615baSAndrew Gallatin abort:
5078fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5088fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5098fe615baSAndrew Gallatin 			      test, status);
5108fe615baSAndrew Gallatin 
5118fe615baSAndrew Gallatin 	return status;
5128fe615baSAndrew Gallatin }
5138fe615baSAndrew Gallatin 
514b2fc195eSAndrew Gallatin /*
515b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
516b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
517b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
518b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
519b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
520b2fc195eSAndrew Gallatin  *
521b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
522b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
523b2fc195eSAndrew Gallatin  *
524b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
525b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
526b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
527b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
5281e413cf9SAndrew Gallatin  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
529b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
5301e413cf9SAndrew Gallatin  * firmware image, and set tx_boundary to 4KB.
531b2fc195eSAndrew Gallatin  */
532b2fc195eSAndrew Gallatin 
5338fe615baSAndrew Gallatin static int
5348fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5358fe615baSAndrew Gallatin {
5368fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5378fe615baSAndrew Gallatin 	int reg, status;
5388fe615baSAndrew Gallatin 	uint16_t pectl;
5398fe615baSAndrew Gallatin 
5401e413cf9SAndrew Gallatin 	sc->tx_boundary = 4096;
5418fe615baSAndrew Gallatin 	/*
5428fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5438fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5448fe615baSAndrew Gallatin 	 */
5458fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5468fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5478fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5488fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5498fe615baSAndrew Gallatin 				      pectl);
5501e413cf9SAndrew Gallatin 			sc->tx_boundary = 2048;
5518fe615baSAndrew Gallatin 		}
5528fe615baSAndrew Gallatin 	}
5538fe615baSAndrew Gallatin 
5548fe615baSAndrew Gallatin 	/*
5558fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5568fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5578fe615baSAndrew Gallatin 	 */
5588fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5591e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 1);
5608fe615baSAndrew Gallatin 	if (status != 0) {
5618fe615baSAndrew Gallatin 		return status;
5628fe615baSAndrew Gallatin 	}
5638fe615baSAndrew Gallatin 
5648fe615baSAndrew Gallatin 	/*
5658fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5668fe615baSAndrew Gallatin 	 */
5678fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5688fe615baSAndrew Gallatin 
5698fe615baSAndrew Gallatin 	/*
5708fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5718fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5728fe615baSAndrew Gallatin 	 */
5738fe615baSAndrew Gallatin 
5748fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5758fe615baSAndrew Gallatin 	if (status == 0)
5768fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5778fe615baSAndrew Gallatin 
5788fe615baSAndrew Gallatin 	if (status != E2BIG)
5798fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5808fe615baSAndrew Gallatin 	if (status == ENOSYS)
5818fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5828fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5838fe615baSAndrew Gallatin 	return status;
5848fe615baSAndrew Gallatin }
5858fe615baSAndrew Gallatin 
5868fe615baSAndrew Gallatin static int
5876d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
588b2fc195eSAndrew Gallatin {
5898fe615baSAndrew Gallatin 	int aligned = 0;
590b2fc195eSAndrew Gallatin 
591d91b1b49SAndrew Gallatin 
592d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
593d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
594d91b1b49SAndrew Gallatin 			aligned = 1;
595d91b1b49SAndrew Gallatin 		else
596d91b1b49SAndrew Gallatin 			aligned = 0;
597d91b1b49SAndrew Gallatin 		if (mxge_verbose)
598d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
599d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
600d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
601d91b1b49SAndrew Gallatin 		goto abort;
602d91b1b49SAndrew Gallatin 	}
603d91b1b49SAndrew Gallatin 
604d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
605d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
606d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
607d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
608d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
609d91b1b49SAndrew Gallatin 			      sc->link_width);
610d91b1b49SAndrew Gallatin 		aligned = 1;
611d91b1b49SAndrew Gallatin 		goto abort;
612d91b1b49SAndrew Gallatin 	}
613d91b1b49SAndrew Gallatin 
6148fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
6158fe615baSAndrew Gallatin 		return 0;
616b2fc195eSAndrew Gallatin 
617b2fc195eSAndrew Gallatin abort:
618b2fc195eSAndrew Gallatin 	if (aligned) {
6196d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
6201e413cf9SAndrew Gallatin 		sc->tx_boundary = 4096;
621b2fc195eSAndrew Gallatin 	} else {
6226d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
6231e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
624b2fc195eSAndrew Gallatin 	}
6251e413cf9SAndrew Gallatin 	return (mxge_load_firmware(sc, 0));
626b2fc195eSAndrew Gallatin }
627b2fc195eSAndrew Gallatin 
628b2fc195eSAndrew Gallatin union qualhack
629b2fc195eSAndrew Gallatin {
630b2fc195eSAndrew Gallatin         const char *ro_char;
631b2fc195eSAndrew Gallatin         char *rw_char;
632b2fc195eSAndrew Gallatin };
633b2fc195eSAndrew Gallatin 
6344da0d523SAndrew Gallatin static int
6354da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6364da0d523SAndrew Gallatin {
637b824b7d8SAndrew Gallatin 
6384da0d523SAndrew Gallatin 
6394da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6404da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6414da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6424da0d523SAndrew Gallatin 		return EIO;
6434da0d523SAndrew Gallatin 	}
6444da0d523SAndrew Gallatin 
6454da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6464da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6474da0d523SAndrew Gallatin 	if (mxge_verbose)
6484da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6494da0d523SAndrew Gallatin 
650b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
651b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6524da0d523SAndrew Gallatin 
653b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
654b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6554da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6564da0d523SAndrew Gallatin 			      sc->fw_version);
6574da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6584da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6594da0d523SAndrew Gallatin 		return EINVAL;
6604da0d523SAndrew Gallatin 	}
6614da0d523SAndrew Gallatin 	return 0;
6624da0d523SAndrew Gallatin 
6634da0d523SAndrew Gallatin }
664b2fc195eSAndrew Gallatin 
665f9ae0280SAndrew Gallatin static void *
666f9ae0280SAndrew Gallatin z_alloc(void *nil, u_int items, u_int size)
667f9ae0280SAndrew Gallatin {
668f9ae0280SAndrew Gallatin         void *ptr;
669f9ae0280SAndrew Gallatin 
670f9ae0280SAndrew Gallatin         ptr = malloc(items * size, M_TEMP, M_NOWAIT);
671f9ae0280SAndrew Gallatin         return ptr;
672f9ae0280SAndrew Gallatin }
673f9ae0280SAndrew Gallatin 
674f9ae0280SAndrew Gallatin static void
675f9ae0280SAndrew Gallatin z_free(void *nil, void *ptr)
676f9ae0280SAndrew Gallatin {
677f9ae0280SAndrew Gallatin         free(ptr, M_TEMP);
678f9ae0280SAndrew Gallatin }
679f9ae0280SAndrew Gallatin 
680f9ae0280SAndrew Gallatin 
681b2fc195eSAndrew Gallatin static int
6826d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
683b2fc195eSAndrew Gallatin {
684f9ae0280SAndrew Gallatin 	z_stream zs;
685f9ae0280SAndrew Gallatin 	char *inflate_buffer;
68633d54970SLuigi Rizzo 	const struct firmware *fw;
687b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
688b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
689b2fc195eSAndrew Gallatin 	int status;
6904da0d523SAndrew Gallatin 	unsigned int i;
6914da0d523SAndrew Gallatin 	char dummy;
692f9ae0280SAndrew Gallatin 	size_t fw_len;
693b2fc195eSAndrew Gallatin 
694b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
695b2fc195eSAndrew Gallatin 	if (fw == NULL) {
696b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
697b2fc195eSAndrew Gallatin 			      sc->fw_name);
698b2fc195eSAndrew Gallatin 		return ENOENT;
699b2fc195eSAndrew Gallatin 	}
700b2fc195eSAndrew Gallatin 
701f9ae0280SAndrew Gallatin 
702f9ae0280SAndrew Gallatin 
703f9ae0280SAndrew Gallatin 	/* setup zlib and decompress f/w */
704f9ae0280SAndrew Gallatin 	bzero(&zs, sizeof (zs));
705f9ae0280SAndrew Gallatin 	zs.zalloc = z_alloc;
706f9ae0280SAndrew Gallatin 	zs.zfree = z_free;
707f9ae0280SAndrew Gallatin 	status = inflateInit(&zs);
708f9ae0280SAndrew Gallatin 	if (status != Z_OK) {
709b2fc195eSAndrew Gallatin 		status = EIO;
710b2fc195eSAndrew Gallatin 		goto abort_with_fw;
711b2fc195eSAndrew Gallatin 	}
712f9ae0280SAndrew Gallatin 
713f9ae0280SAndrew Gallatin 	/* the uncompressed size is stored as the firmware version,
714f9ae0280SAndrew Gallatin 	   which would otherwise go unused */
715f9ae0280SAndrew Gallatin 	fw_len = (size_t) fw->version;
716f9ae0280SAndrew Gallatin 	inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT);
717f9ae0280SAndrew Gallatin 	if (inflate_buffer == NULL)
718f9ae0280SAndrew Gallatin 		goto abort_with_zs;
719f9ae0280SAndrew Gallatin 	zs.avail_in = fw->datasize;
720f9ae0280SAndrew Gallatin 	zs.next_in = __DECONST(char *, fw->data);
721f9ae0280SAndrew Gallatin 	zs.avail_out = fw_len;
722f9ae0280SAndrew Gallatin 	zs.next_out = inflate_buffer;
723f9ae0280SAndrew Gallatin 	status = inflate(&zs, Z_FINISH);
724f9ae0280SAndrew Gallatin 	if (status != Z_STREAM_END) {
725f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "zlib %d\n", status);
726f9ae0280SAndrew Gallatin 		status = EIO;
727f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
728f9ae0280SAndrew Gallatin 	}
729f9ae0280SAndrew Gallatin 
730f9ae0280SAndrew Gallatin 	/* check id */
731f9ae0280SAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
732f9ae0280SAndrew Gallatin 			     (inflate_buffer + MCP_HEADER_PTR_OFFSET));
733f9ae0280SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
734f9ae0280SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
735f9ae0280SAndrew Gallatin 		status = EIO;
736f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
737f9ae0280SAndrew Gallatin 	}
738f9ae0280SAndrew Gallatin 	hdr = (const void*)(inflate_buffer + hdr_offset);
739b2fc195eSAndrew Gallatin 
7404da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
7414da0d523SAndrew Gallatin 	if (status != 0)
742f9ae0280SAndrew Gallatin 		goto abort_with_buffer;
743b2fc195eSAndrew Gallatin 
744b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
745f9ae0280SAndrew Gallatin 	for (i = 0; i < fw_len; i += 256) {
7464da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
747f9ae0280SAndrew Gallatin 			      inflate_buffer + i,
748f9ae0280SAndrew Gallatin 			      min(256U, (unsigned)(fw_len - i)));
7494da0d523SAndrew Gallatin 		mb();
7504da0d523SAndrew Gallatin 		dummy = *sc->sram;
7514da0d523SAndrew Gallatin 		mb();
7524da0d523SAndrew Gallatin 	}
753b2fc195eSAndrew Gallatin 
754f9ae0280SAndrew Gallatin 	*limit = fw_len;
755b2fc195eSAndrew Gallatin 	status = 0;
756f9ae0280SAndrew Gallatin abort_with_buffer:
757f9ae0280SAndrew Gallatin 	free(inflate_buffer, M_TEMP);
758f9ae0280SAndrew Gallatin abort_with_zs:
759f9ae0280SAndrew Gallatin 	inflateEnd(&zs);
760b2fc195eSAndrew Gallatin abort_with_fw:
761b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
762b2fc195eSAndrew Gallatin 	return status;
763b2fc195eSAndrew Gallatin }
764b2fc195eSAndrew Gallatin 
765b2fc195eSAndrew Gallatin /*
766b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
767b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
768b2fc195eSAndrew Gallatin  */
769b2fc195eSAndrew Gallatin 
770b2fc195eSAndrew Gallatin static void
7716d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
772b2fc195eSAndrew Gallatin {
773b2fc195eSAndrew Gallatin 	char buf_bytes[72];
774b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
775b2fc195eSAndrew Gallatin 	volatile char *submit;
776b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
777b2fc195eSAndrew Gallatin 	int i;
778b2fc195eSAndrew Gallatin 
779b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
780b2fc195eSAndrew Gallatin 
781b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
782b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
783b2fc195eSAndrew Gallatin 	*confirm = 0;
784b2fc195eSAndrew Gallatin 	mb();
785b2fc195eSAndrew Gallatin 
786b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
787b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
788b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
789b2fc195eSAndrew Gallatin 	*/
790b2fc195eSAndrew Gallatin 
7916d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7926d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
793b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
794b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
795b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7966d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7976d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
798b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
799b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
800b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
801b2fc195eSAndrew Gallatin 
802b2fc195eSAndrew Gallatin 
8030fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
804b2fc195eSAndrew Gallatin 
8056d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
806b2fc195eSAndrew Gallatin 	mb();
807b2fc195eSAndrew Gallatin 	DELAY(1000);
808b2fc195eSAndrew Gallatin 	mb();
809b2fc195eSAndrew Gallatin 	i = 0;
810b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
811b2fc195eSAndrew Gallatin 		DELAY(1000);
812b2fc195eSAndrew Gallatin 		i++;
813b2fc195eSAndrew Gallatin 	}
814b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
815b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
816b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
817b2fc195eSAndrew Gallatin 			      *confirm);
818b2fc195eSAndrew Gallatin 	}
819b2fc195eSAndrew Gallatin 	return;
820b2fc195eSAndrew Gallatin }
821b2fc195eSAndrew Gallatin 
822b2fc195eSAndrew Gallatin static int
8236d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
824b2fc195eSAndrew Gallatin {
825b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
826b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
827b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
8280fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
829b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
830e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
831b2fc195eSAndrew Gallatin 
832b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
833b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
834b2fc195eSAndrew Gallatin 
835b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
836b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
837b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
838b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
8396d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8406d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
841b2fc195eSAndrew Gallatin 
842b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
843b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
844a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
845b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
846b2fc195eSAndrew Gallatin 	mb();
8476d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
848b2fc195eSAndrew Gallatin 
8495e7d8541SAndrew Gallatin 	/* wait up to 20ms */
850e0501fd0SAndrew Gallatin 	err = EAGAIN;
8515e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
852b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
853b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
854b2fc195eSAndrew Gallatin 		mb();
855e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
856e0501fd0SAndrew Gallatin 		case 0:
857b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
858e0501fd0SAndrew Gallatin 			err = 0;
859e0501fd0SAndrew Gallatin 			break;
860e0501fd0SAndrew Gallatin 		case 0xffffffff:
861e0501fd0SAndrew Gallatin 			DELAY(1000);
862e0501fd0SAndrew Gallatin 			break;
863e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
864e0501fd0SAndrew Gallatin 			err = ENOSYS;
865e0501fd0SAndrew Gallatin 			break;
866e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
867e0501fd0SAndrew Gallatin 			err = E2BIG;
868e0501fd0SAndrew Gallatin 			break;
869c587e59fSAndrew Gallatin 		case MXGEFW_CMD_ERROR_BUSY:
870c587e59fSAndrew Gallatin 			err = EBUSY;
871c587e59fSAndrew Gallatin 			break;
872e0501fd0SAndrew Gallatin 		default:
873b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8746d87a65dSAndrew Gallatin 				      "mxge: command %d "
875b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
876b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
877e0501fd0SAndrew Gallatin 			err = ENXIO;
878e0501fd0SAndrew Gallatin 			break;
879b2fc195eSAndrew Gallatin 		}
880e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
881e0501fd0SAndrew Gallatin 			break;
882b2fc195eSAndrew Gallatin 	}
883e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8846d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
885b2fc195eSAndrew Gallatin 			      "result = %d\n",
886b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
887e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
888e0501fd0SAndrew Gallatin 	return err;
889b2fc195eSAndrew Gallatin }
890b2fc195eSAndrew Gallatin 
8914da0d523SAndrew Gallatin static int
8924da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8934da0d523SAndrew Gallatin {
8944da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8954da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8964da0d523SAndrew Gallatin 	size_t hdr_offset;
8974da0d523SAndrew Gallatin 	int status;
8984da0d523SAndrew Gallatin 
8994da0d523SAndrew Gallatin 	/* find running firmware header */
9004da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
9014da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9024da0d523SAndrew Gallatin 
9034da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9044da0d523SAndrew Gallatin 		device_printf(sc->dev,
9054da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
9064da0d523SAndrew Gallatin 			      (int)hdr_offset);
9074da0d523SAndrew Gallatin 		return EIO;
9084da0d523SAndrew Gallatin 	}
9094da0d523SAndrew Gallatin 
9104da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
9114da0d523SAndrew Gallatin 	 * validate firmware */
9124da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
9134da0d523SAndrew Gallatin 	if (hdr == NULL) {
9144da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
9154da0d523SAndrew Gallatin 		return ENOMEM;
9164da0d523SAndrew Gallatin 	}
9174da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9184da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
9194da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
9204da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
9214da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
922b824b7d8SAndrew Gallatin 
923b824b7d8SAndrew Gallatin 	/*
924b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
925b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
926b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
927b824b7d8SAndrew Gallatin 	 */
928b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
929b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
930b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
931b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
932b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
933b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
934b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
935b824b7d8SAndrew Gallatin 	}
936b824b7d8SAndrew Gallatin 
9374da0d523SAndrew Gallatin 	return status;
9384da0d523SAndrew Gallatin }
9394da0d523SAndrew Gallatin 
940b2fc195eSAndrew Gallatin 
941b2fc195eSAndrew Gallatin static int
9421e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt)
943b2fc195eSAndrew Gallatin {
944b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
945b2fc195eSAndrew Gallatin 	volatile char *submit;
946b2fc195eSAndrew Gallatin 	char buf_bytes[72];
947b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
948b2fc195eSAndrew Gallatin 	int status, i;
949b2fc195eSAndrew Gallatin 
950b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
951b2fc195eSAndrew Gallatin 
952b2fc195eSAndrew Gallatin 	size = sc->sram_size;
9536d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
954b2fc195eSAndrew Gallatin 	if (status) {
9551e413cf9SAndrew Gallatin 		if (!adopt)
9561e413cf9SAndrew Gallatin 			return status;
9574da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
9584da0d523SAndrew Gallatin 		   it is new enough */
9594da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
9604da0d523SAndrew Gallatin 		if (status) {
9614da0d523SAndrew Gallatin 			device_printf(sc->dev,
9624da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
963b2fc195eSAndrew Gallatin 			return status;
964b2fc195eSAndrew Gallatin 		}
9654da0d523SAndrew Gallatin 		device_printf(sc->dev,
9664da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
9671e413cf9SAndrew Gallatin 		if (sc->tx_boundary == 4096) {
9684da0d523SAndrew Gallatin 			device_printf(sc->dev,
9694da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
9704da0d523SAndrew Gallatin 				 ".  For optimal\n");
9714da0d523SAndrew Gallatin 			device_printf(sc->dev,
9724da0d523SAndrew Gallatin 				 "performance consider loading optimized "
9734da0d523SAndrew Gallatin 				 "firmware\n");
9744da0d523SAndrew Gallatin 		}
975d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
9761e413cf9SAndrew Gallatin 		sc->tx_boundary = 2048;
977d91b1b49SAndrew Gallatin 		return 0;
9784da0d523SAndrew Gallatin 	}
979b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
980b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
981b2fc195eSAndrew Gallatin 	*confirm = 0;
982b2fc195eSAndrew Gallatin 	mb();
983b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
984b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
985b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
986b2fc195eSAndrew Gallatin 	*/
987b2fc195eSAndrew Gallatin 
9886d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9896d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
990b2fc195eSAndrew Gallatin 
991b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
992b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
993b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
994b2fc195eSAndrew Gallatin 
995b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
996b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
997b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
998b2fc195eSAndrew Gallatin 	*/
999b2fc195eSAndrew Gallatin 					/* where the code starts*/
10006d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
1001b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
1002b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
1003b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
1004b2fc195eSAndrew Gallatin 
10050fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10066d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
1007b2fc195eSAndrew Gallatin 	mb();
1008b2fc195eSAndrew Gallatin 	DELAY(1000);
1009b2fc195eSAndrew Gallatin 	mb();
1010b2fc195eSAndrew Gallatin 	i = 0;
1011b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
1012b2fc195eSAndrew Gallatin 		DELAY(1000*10);
1013b2fc195eSAndrew Gallatin 		i++;
1014b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
1015b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
1016b2fc195eSAndrew Gallatin 	}
1017b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
1018b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
1019b2fc195eSAndrew Gallatin 			confirm, *confirm);
1020b2fc195eSAndrew Gallatin 
1021b2fc195eSAndrew Gallatin 		return ENXIO;
1022b2fc195eSAndrew Gallatin 	}
1023b2fc195eSAndrew Gallatin 	return 0;
1024b2fc195eSAndrew Gallatin }
1025b2fc195eSAndrew Gallatin 
1026b2fc195eSAndrew Gallatin static int
10276d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
1028b2fc195eSAndrew Gallatin {
10296d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1030b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
1031b2fc195eSAndrew Gallatin 	int status;
1032b2fc195eSAndrew Gallatin 
1033b2fc195eSAndrew Gallatin 
1034b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
1035b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
1036b2fc195eSAndrew Gallatin 
1037b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
1038b2fc195eSAndrew Gallatin 
10395e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
1040b2fc195eSAndrew Gallatin 	return status;
1041b2fc195eSAndrew Gallatin }
1042b2fc195eSAndrew Gallatin 
1043b2fc195eSAndrew Gallatin static int
10446d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
1045b2fc195eSAndrew Gallatin {
10466d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1047b2fc195eSAndrew Gallatin 	int status;
1048b2fc195eSAndrew Gallatin 
1049b2fc195eSAndrew Gallatin 	if (pause)
10505e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
1051b2fc195eSAndrew Gallatin 				       &cmd);
1052b2fc195eSAndrew Gallatin 	else
10535e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
1054b2fc195eSAndrew Gallatin 				       &cmd);
1055b2fc195eSAndrew Gallatin 
1056b2fc195eSAndrew Gallatin 	if (status) {
1057b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
1058b2fc195eSAndrew Gallatin 		return ENXIO;
1059b2fc195eSAndrew Gallatin 	}
1060b2fc195eSAndrew Gallatin 	sc->pause = pause;
1061b2fc195eSAndrew Gallatin 	return 0;
1062b2fc195eSAndrew Gallatin }
1063b2fc195eSAndrew Gallatin 
1064b2fc195eSAndrew Gallatin static void
10656d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
1066b2fc195eSAndrew Gallatin {
10676d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
1068b2fc195eSAndrew Gallatin 	int status;
1069b2fc195eSAndrew Gallatin 
10701e413cf9SAndrew Gallatin 	if (mxge_always_promisc)
10711e413cf9SAndrew Gallatin 		promisc = 1;
10721e413cf9SAndrew Gallatin 
1073b2fc195eSAndrew Gallatin 	if (promisc)
10745e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
1075b2fc195eSAndrew Gallatin 				       &cmd);
1076b2fc195eSAndrew Gallatin 	else
10775e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1078b2fc195eSAndrew Gallatin 				       &cmd);
1079b2fc195eSAndrew Gallatin 
1080b2fc195eSAndrew Gallatin 	if (status) {
1081b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1082b2fc195eSAndrew Gallatin 	}
1083b2fc195eSAndrew Gallatin }
1084b2fc195eSAndrew Gallatin 
10850fa7f681SAndrew Gallatin static void
10860fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10870fa7f681SAndrew Gallatin {
10880fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10890fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10900fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10910fa7f681SAndrew Gallatin 	int err;
10920fa7f681SAndrew Gallatin 
10930fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10940fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10950fa7f681SAndrew Gallatin 		return;
10960fa7f681SAndrew Gallatin 
10970fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10980fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10990fa7f681SAndrew Gallatin 	if (err != 0) {
11000fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11010fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
11020fa7f681SAndrew Gallatin 		return;
11030fa7f681SAndrew Gallatin 	}
11040fa7f681SAndrew Gallatin 
1105b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1106b824b7d8SAndrew Gallatin 		return;
11070fa7f681SAndrew Gallatin 
11080fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
11090fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
11100fa7f681SAndrew Gallatin 		return;
11110fa7f681SAndrew Gallatin 
11120fa7f681SAndrew Gallatin 	/* Flush all the filters */
11130fa7f681SAndrew Gallatin 
11140fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11150fa7f681SAndrew Gallatin 	if (err != 0) {
11160fa7f681SAndrew Gallatin 		device_printf(sc->dev,
11170fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11180fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
11190fa7f681SAndrew Gallatin 		return;
11200fa7f681SAndrew Gallatin 	}
11210fa7f681SAndrew Gallatin 
11220fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
11230fa7f681SAndrew Gallatin 
11240fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
11250fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11260fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
11270fa7f681SAndrew Gallatin 			continue;
11280fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11290fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
11300fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11310fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
11320fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
11330fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
11340fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11350fa7f681SAndrew Gallatin 		if (err != 0) {
11360fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
11370fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11380fa7f681SAndrew Gallatin 			       "%d\t", err);
11390fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
11400fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
11410fa7f681SAndrew Gallatin 			return;
11420fa7f681SAndrew Gallatin 		}
11430fa7f681SAndrew Gallatin 	}
11440fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
11450fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
11460fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11470fa7f681SAndrew Gallatin 	if (err != 0) {
11480fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11490fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
11500fa7f681SAndrew Gallatin 	}
11510fa7f681SAndrew Gallatin }
11520fa7f681SAndrew Gallatin 
1153b2fc195eSAndrew Gallatin static int
1154053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1155053e637fSAndrew Gallatin {
1156053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1157053e637fSAndrew Gallatin 	int status;
1158053e637fSAndrew Gallatin 
1159c792928fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
1160c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1161053e637fSAndrew Gallatin 
1162053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1163053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1164053e637fSAndrew Gallatin 	cmd.data0 = 0;
1165053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1166053e637fSAndrew Gallatin 			       &cmd);
1167053e637fSAndrew Gallatin 	if (status == 0)
1168c792928fSAndrew Gallatin 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
1169053e637fSAndrew Gallatin 
1170053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1171053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1172053e637fSAndrew Gallatin }
1173053e637fSAndrew Gallatin 
1174053e637fSAndrew Gallatin static int
1175adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup)
1176b2fc195eSAndrew Gallatin {
11771e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
11781e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done;
11791e413cf9SAndrew Gallatin 	volatile uint32_t *irq_claim;
11806d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11811e413cf9SAndrew Gallatin 	int slice, status;
1182b2fc195eSAndrew Gallatin 
1183b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1184b2fc195eSAndrew Gallatin 	   is alive */
1185b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11865e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1187b2fc195eSAndrew Gallatin 	if (status != 0) {
1188b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1189b2fc195eSAndrew Gallatin 		return ENXIO;
1190b2fc195eSAndrew Gallatin 	}
1191b2fc195eSAndrew Gallatin 
1192091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1193091feecdSAndrew Gallatin 
11941e413cf9SAndrew Gallatin 
11951e413cf9SAndrew Gallatin 	/* set the intrq size */
11961e413cf9SAndrew Gallatin 	cmd.data0 = sc->rx_ring_size;
11971e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11981e413cf9SAndrew Gallatin 
11991e413cf9SAndrew Gallatin 	/*
12001e413cf9SAndrew Gallatin 	 * Even though we already know how many slices are supported
12011e413cf9SAndrew Gallatin 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12021e413cf9SAndrew Gallatin 	 * has magic side effects, and must be called after a reset.
12031e413cf9SAndrew Gallatin 	 * It must be called prior to calling any RSS related cmds,
12041e413cf9SAndrew Gallatin 	 * including assigning an interrupt queue for anything but
12051e413cf9SAndrew Gallatin 	 * slice 0.  It must also be called *after*
12061e413cf9SAndrew Gallatin 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12071e413cf9SAndrew Gallatin 	 * the firmware to compute offsets.
12081e413cf9SAndrew Gallatin 	 */
12091e413cf9SAndrew Gallatin 
12101e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
12111e413cf9SAndrew Gallatin 		/* ask the maximum number of slices it supports */
12121e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12131e413cf9SAndrew Gallatin 					   &cmd);
12141e413cf9SAndrew Gallatin 		if (status != 0) {
12151e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12161e413cf9SAndrew Gallatin 				      "failed to get number of slices\n");
12171e413cf9SAndrew Gallatin 			return status;
12181e413cf9SAndrew Gallatin 		}
12191e413cf9SAndrew Gallatin 		/*
12201e413cf9SAndrew Gallatin 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12211e413cf9SAndrew Gallatin 		 * to setting up the interrupt queue DMA
12221e413cf9SAndrew Gallatin 		 */
12231e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
12241e413cf9SAndrew Gallatin 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
12251e413cf9SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12261e413cf9SAndrew Gallatin 					   &cmd);
12271e413cf9SAndrew Gallatin 		if (status != 0) {
12281e413cf9SAndrew Gallatin 			device_printf(sc->dev,
12291e413cf9SAndrew Gallatin 				      "failed to set number of slices\n");
12301e413cf9SAndrew Gallatin 			return status;
12311e413cf9SAndrew Gallatin 		}
12321e413cf9SAndrew Gallatin 	}
12331e413cf9SAndrew Gallatin 
12341e413cf9SAndrew Gallatin 
1235adae7080SAndrew Gallatin 	if (interrupts_setup) {
1236b2fc195eSAndrew Gallatin 		/* Now exchange information about interrupts  */
12371e413cf9SAndrew Gallatin 		for (slice = 0; slice < sc->num_slices; slice++) {
12381e413cf9SAndrew Gallatin 			rx_done = &sc->ss[slice].rx_done;
12391e413cf9SAndrew Gallatin 			memset(rx_done->entry, 0, sc->rx_ring_size);
12401e413cf9SAndrew Gallatin 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12411e413cf9SAndrew Gallatin 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12421e413cf9SAndrew Gallatin 			cmd.data2 = slice;
12431e413cf9SAndrew Gallatin 			status |= mxge_send_cmd(sc,
12441e413cf9SAndrew Gallatin 						MXGEFW_CMD_SET_INTRQ_DMA,
12451e413cf9SAndrew Gallatin 						&cmd);
12461e413cf9SAndrew Gallatin 		}
1247adae7080SAndrew Gallatin 	}
1248b2fc195eSAndrew Gallatin 
12496d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
12505e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12515e7d8541SAndrew Gallatin 
12525e7d8541SAndrew Gallatin 
12535e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12545e7d8541SAndrew Gallatin 
12555e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12561e413cf9SAndrew Gallatin 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12575e7d8541SAndrew Gallatin 
12585e7d8541SAndrew Gallatin 
12595e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12606d87a65dSAndrew Gallatin 				&cmd);
12615e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1262b2fc195eSAndrew Gallatin 	if (status != 0) {
1263b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1264b2fc195eSAndrew Gallatin 		return status;
1265b2fc195eSAndrew Gallatin 	}
1266b2fc195eSAndrew Gallatin 
12675e7d8541SAndrew Gallatin 
12685e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12695e7d8541SAndrew Gallatin 
12705e7d8541SAndrew Gallatin 
12715e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
12728fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12735e7d8541SAndrew Gallatin 
12741e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
12751e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
12761e413cf9SAndrew Gallatin 
12771e413cf9SAndrew Gallatin 		ss->irq_claim = irq_claim + (2 * slice);
1278b2fc195eSAndrew Gallatin 		/* reset mcp/driver shared state back to 0 */
12791e413cf9SAndrew Gallatin 		ss->rx_done.idx = 0;
12801e413cf9SAndrew Gallatin 		ss->rx_done.cnt = 0;
12811e413cf9SAndrew Gallatin 		ss->tx.req = 0;
12821e413cf9SAndrew Gallatin 		ss->tx.done = 0;
12831e413cf9SAndrew Gallatin 		ss->tx.pkt_done = 0;
12841e413cf9SAndrew Gallatin 		ss->tx.wake = 0;
12851e413cf9SAndrew Gallatin 		ss->tx.defrag = 0;
12861e413cf9SAndrew Gallatin 		ss->tx.stall = 0;
12871e413cf9SAndrew Gallatin 		ss->rx_big.cnt = 0;
12881e413cf9SAndrew Gallatin 		ss->rx_small.cnt = 0;
12891e413cf9SAndrew Gallatin 		ss->lro_bad_csum = 0;
12901e413cf9SAndrew Gallatin 		ss->lro_queued = 0;
12911e413cf9SAndrew Gallatin 		ss->lro_flushed = 0;
12921e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
12931e413cf9SAndrew Gallatin 			ss->fw_stats->valid = 0;
12941e413cf9SAndrew Gallatin 			ss->fw_stats->send_done_count = 0;
12951e413cf9SAndrew Gallatin 		}
12961e413cf9SAndrew Gallatin 	}
1297b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
12986d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
12996d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
13006d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
13010fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1302b2fc195eSAndrew Gallatin 	return status;
1303b2fc195eSAndrew Gallatin }
1304b2fc195eSAndrew Gallatin 
1305b2fc195eSAndrew Gallatin static int
13066d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1307b2fc195eSAndrew Gallatin {
13086d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1309b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1310b2fc195eSAndrew Gallatin         int err;
1311b2fc195eSAndrew Gallatin 
1312b2fc195eSAndrew Gallatin         sc = arg1;
1313b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1314b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1315b2fc195eSAndrew Gallatin         if (err != 0) {
1316b2fc195eSAndrew Gallatin                 return err;
1317b2fc195eSAndrew Gallatin         }
1318b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1319b2fc195eSAndrew Gallatin                 return 0;
1320b2fc195eSAndrew Gallatin 
1321b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1322b2fc195eSAndrew Gallatin                 return EINVAL;
1323b2fc195eSAndrew Gallatin 
1324a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13255e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1326b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
13275e7d8541SAndrew Gallatin 
1328a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1329b2fc195eSAndrew Gallatin         return err;
1330b2fc195eSAndrew Gallatin }
1331b2fc195eSAndrew Gallatin 
1332b2fc195eSAndrew Gallatin static int
13336d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1334b2fc195eSAndrew Gallatin {
13356d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1336b2fc195eSAndrew Gallatin         unsigned int enabled;
1337b2fc195eSAndrew Gallatin         int err;
1338b2fc195eSAndrew Gallatin 
1339b2fc195eSAndrew Gallatin         sc = arg1;
1340b2fc195eSAndrew Gallatin         enabled = sc->pause;
1341b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1342b2fc195eSAndrew Gallatin         if (err != 0) {
1343b2fc195eSAndrew Gallatin                 return err;
1344b2fc195eSAndrew Gallatin         }
1345b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1346b2fc195eSAndrew Gallatin                 return 0;
1347b2fc195eSAndrew Gallatin 
1348a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
13496d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1350a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1351b2fc195eSAndrew Gallatin         return err;
1352b2fc195eSAndrew Gallatin }
1353b2fc195eSAndrew Gallatin 
1354b2fc195eSAndrew Gallatin static int
1355f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
1356f04b33f8SAndrew Gallatin {
1357f04b33f8SAndrew Gallatin 	struct ifnet *ifp;
1358c587e59fSAndrew Gallatin 	int err = 0;
1359f04b33f8SAndrew Gallatin 
1360f04b33f8SAndrew Gallatin 	ifp = sc->ifp;
1361f04b33f8SAndrew Gallatin 	if (lro_cnt == 0)
1362f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
1363f04b33f8SAndrew Gallatin 	else
1364f04b33f8SAndrew Gallatin 		ifp->if_capenable |= IFCAP_LRO;
1365f04b33f8SAndrew Gallatin 	sc->lro_cnt = lro_cnt;
1366c587e59fSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1367f04b33f8SAndrew Gallatin 		callout_stop(&sc->co_hdl);
1368f04b33f8SAndrew Gallatin 		mxge_close(sc);
1369f04b33f8SAndrew Gallatin 		err = mxge_open(sc);
1370f04b33f8SAndrew Gallatin 		if (err == 0)
1371f04b33f8SAndrew Gallatin 			callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
1372c587e59fSAndrew Gallatin 	}
1373f04b33f8SAndrew Gallatin 	return err;
1374f04b33f8SAndrew Gallatin }
1375f04b33f8SAndrew Gallatin 
1376f04b33f8SAndrew Gallatin static int
1377276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS)
1378276edd10SAndrew Gallatin {
1379276edd10SAndrew Gallatin 	mxge_softc_t *sc;
1380276edd10SAndrew Gallatin 	unsigned int lro_cnt;
1381276edd10SAndrew Gallatin 	int err;
1382276edd10SAndrew Gallatin 
1383276edd10SAndrew Gallatin 	sc = arg1;
1384276edd10SAndrew Gallatin 	lro_cnt = sc->lro_cnt;
1385276edd10SAndrew Gallatin 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
1386276edd10SAndrew Gallatin 	if (err != 0)
1387276edd10SAndrew Gallatin 		return err;
1388276edd10SAndrew Gallatin 
1389276edd10SAndrew Gallatin 	if (lro_cnt == sc->lro_cnt)
1390276edd10SAndrew Gallatin 		return 0;
1391276edd10SAndrew Gallatin 
1392276edd10SAndrew Gallatin 	if (lro_cnt > 128)
1393276edd10SAndrew Gallatin 		return EINVAL;
1394276edd10SAndrew Gallatin 
1395276edd10SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
1396f04b33f8SAndrew Gallatin 	err = mxge_change_lro_locked(sc, lro_cnt);
1397276edd10SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1398276edd10SAndrew Gallatin 	return err;
1399276edd10SAndrew Gallatin }
1400276edd10SAndrew Gallatin 
1401276edd10SAndrew Gallatin static int
14026d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1403b2fc195eSAndrew Gallatin {
1404b2fc195eSAndrew Gallatin         int err;
1405b2fc195eSAndrew Gallatin 
1406b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1407b2fc195eSAndrew Gallatin                 return EFAULT;
1408b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1409b2fc195eSAndrew Gallatin         arg1 = NULL;
1410b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1411b2fc195eSAndrew Gallatin 
1412b2fc195eSAndrew Gallatin         return err;
1413b2fc195eSAndrew Gallatin }
1414b2fc195eSAndrew Gallatin 
1415b2fc195eSAndrew Gallatin static void
14161e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc)
14171e413cf9SAndrew Gallatin {
14181e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14191e413cf9SAndrew Gallatin 	int slice;
14201e413cf9SAndrew Gallatin 
14211e413cf9SAndrew Gallatin 	if (sc->slice_sysctl_tree == NULL)
14221e413cf9SAndrew Gallatin 		return;
14231e413cf9SAndrew Gallatin 
14241e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
14251e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
14261e413cf9SAndrew Gallatin 		if (ss == NULL || ss->sysctl_tree == NULL)
14271e413cf9SAndrew Gallatin 			continue;
14281e413cf9SAndrew Gallatin 		sysctl_ctx_free(&ss->sysctl_ctx);
14291e413cf9SAndrew Gallatin 		ss->sysctl_tree = NULL;
14301e413cf9SAndrew Gallatin 	}
14311e413cf9SAndrew Gallatin 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14321e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree = NULL;
14331e413cf9SAndrew Gallatin }
14341e413cf9SAndrew Gallatin 
14351e413cf9SAndrew Gallatin static void
14366d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1437b2fc195eSAndrew Gallatin {
1438b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1439b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
14405e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
14411e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
14421e413cf9SAndrew Gallatin 	int slice;
14431e413cf9SAndrew Gallatin 	char slice_num[8];
1444b2fc195eSAndrew Gallatin 
1445b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1446b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
14471e413cf9SAndrew Gallatin 	fw = sc->ss[0].fw_stats;
1448b2fc195eSAndrew Gallatin 
14495e7d8541SAndrew Gallatin 	/* random information */
14505e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14515e7d8541SAndrew Gallatin 		       "firmware_version",
14525e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
14535e7d8541SAndrew Gallatin 		       0, "firmware version");
14545e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14555e7d8541SAndrew Gallatin 		       "serial_number",
14565e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
14575e7d8541SAndrew Gallatin 		       0, "serial number");
14585e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14595e7d8541SAndrew Gallatin 		       "product_code",
14605e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
14615e7d8541SAndrew Gallatin 		       0, "product_code");
14625e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1463d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1464d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1465d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1466d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14675e7d8541SAndrew Gallatin 		       "tx_boundary",
14681e413cf9SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx_boundary,
14695e7d8541SAndrew Gallatin 		       0, "tx_boundary");
14705e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1471091feecdSAndrew Gallatin 		       "write_combine",
1472091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1473091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1474091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14755e7d8541SAndrew Gallatin 		       "read_dma_MBs",
14765e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
14775e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
14785e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14795e7d8541SAndrew Gallatin 		       "write_dma_MBs",
14805e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
14815e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
14825e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14835e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
14845e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
14855e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
14865e7d8541SAndrew Gallatin 
14875e7d8541SAndrew Gallatin 
14885e7d8541SAndrew Gallatin 	/* performance related tunables */
1489b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1490b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1491b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14926d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1493b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1494b2fc195eSAndrew Gallatin 
1495b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1496b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1497b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
14986d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1499b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1500b2fc195eSAndrew Gallatin 
1501b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15025e7d8541SAndrew Gallatin 		       "deassert_wait",
15035e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
15045e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1505b2fc195eSAndrew Gallatin 
1506b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1507b2fc195eSAndrew Gallatin 	   Need to swap it */
1508b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1509b2fc195eSAndrew Gallatin 			"link_up",
1510b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15116d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1512b2fc195eSAndrew Gallatin 			"I", "link up");
1513b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1514b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1515b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15166d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1517b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1518b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1519adae7080SAndrew Gallatin 			"dropped_bad_crc32",
1520adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1521adae7080SAndrew Gallatin 			&fw->dropped_bad_crc32,
15226d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1523adae7080SAndrew Gallatin 			"I", "dropped_bad_crc32");
1524adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1525adae7080SAndrew Gallatin 			"dropped_bad_phy",
1526adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1527adae7080SAndrew Gallatin 			&fw->dropped_bad_phy,
1528adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1529adae7080SAndrew Gallatin 			"I", "dropped_bad_phy");
1530b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1531b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1532b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1533b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
15346d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1535b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1536b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1537adae7080SAndrew Gallatin 			"dropped_link_overflow",
1538adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
1539adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1540adae7080SAndrew Gallatin 			"I", "dropped_link_overflow");
1541adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15420fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
15430fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
15440fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
15450fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
15460fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
15470fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1548adae7080SAndrew Gallatin 			"dropped_no_big_buffer",
1549adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15506d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1551adae7080SAndrew Gallatin 			"I", "dropped_no_big_buffer");
1552b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1553b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1554b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1555b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
15566d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1557b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1558b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1559adae7080SAndrew Gallatin 			"dropped_overrun",
1560adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15616d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1562adae7080SAndrew Gallatin 			"I", "dropped_overrun");
1563adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1564adae7080SAndrew Gallatin 			"dropped_pause",
1565adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1566adae7080SAndrew Gallatin 			&fw->dropped_pause,
1567adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1568adae7080SAndrew Gallatin 			"I", "dropped_pause");
1569adae7080SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1570adae7080SAndrew Gallatin 			"dropped_runt",
1571adae7080SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
1572adae7080SAndrew Gallatin 			0, mxge_handle_be32,
1573adae7080SAndrew Gallatin 			"I", "dropped_runt");
1574b2fc195eSAndrew Gallatin 
1575a0394e33SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1576a0394e33SAndrew Gallatin 			"dropped_unicast_filtered",
1577a0394e33SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
1578a0394e33SAndrew Gallatin 			0, mxge_handle_be32,
1579a0394e33SAndrew Gallatin 			"I", "dropped_unicast_filtered");
1580a0394e33SAndrew Gallatin 
15815e7d8541SAndrew Gallatin 	/* verbose printing? */
1582b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15835e7d8541SAndrew Gallatin 		       "verbose",
15845e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
15855e7d8541SAndrew Gallatin 		       0, "verbose printing");
1586b2fc195eSAndrew Gallatin 
1587053e637fSAndrew Gallatin 	/* lro */
1588276edd10SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1589276edd10SAndrew Gallatin 			"lro_cnt",
1590276edd10SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
1591276edd10SAndrew Gallatin 			0, mxge_change_lro,
1592276edd10SAndrew Gallatin 			"I", "number of lro merge queues");
1593053e637fSAndrew Gallatin 
15941e413cf9SAndrew Gallatin 
15951e413cf9SAndrew Gallatin 	/* add counters exported for debugging from all slices */
15961e413cf9SAndrew Gallatin 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15971e413cf9SAndrew Gallatin 	sc->slice_sysctl_tree =
15981e413cf9SAndrew Gallatin 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
15991e413cf9SAndrew Gallatin 				"slice", CTLFLAG_RD, 0, "");
16001e413cf9SAndrew Gallatin 
16011e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
16021e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
16031e413cf9SAndrew Gallatin 		sysctl_ctx_init(&ss->sysctl_ctx);
16041e413cf9SAndrew Gallatin 		ctx = &ss->sysctl_ctx;
16051e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
16061e413cf9SAndrew Gallatin 		sprintf(slice_num, "%d", slice);
16071e413cf9SAndrew Gallatin 		ss->sysctl_tree =
16081e413cf9SAndrew Gallatin 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16091e413cf9SAndrew Gallatin 					CTLFLAG_RD, 0, "");
16101e413cf9SAndrew Gallatin 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
1611053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16121e413cf9SAndrew Gallatin 			       "rx_small_cnt",
16131e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_small.cnt,
16141e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16151e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16161e413cf9SAndrew Gallatin 			       "rx_big_cnt",
16171e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->rx_big.cnt,
16181e413cf9SAndrew Gallatin 			       0, "rx_small_cnt");
16191e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16201e413cf9SAndrew Gallatin 			       "tx_req",
16211e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.req,
16221e413cf9SAndrew Gallatin 			       0, "tx_req");
16231e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16241e413cf9SAndrew Gallatin 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
1625053e637fSAndrew Gallatin 			       0, "number of lro merge queues flushed");
1626053e637fSAndrew Gallatin 
1627053e637fSAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16281e413cf9SAndrew Gallatin 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16291e413cf9SAndrew Gallatin 			       0, "number of frames appended to lro merge"
16301e413cf9SAndrew Gallatin 			       "queues");
1631053e637fSAndrew Gallatin 
16321e413cf9SAndrew Gallatin 		/* only transmit from slice 0 for now */
16331e413cf9SAndrew Gallatin 		if (slice > 0)
16341e413cf9SAndrew Gallatin 			continue;
16351e413cf9SAndrew Gallatin 
16361e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16371e413cf9SAndrew Gallatin 			       "tx_done",
16381e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.done,
16391e413cf9SAndrew Gallatin 			       0, "tx_done");
16401e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16411e413cf9SAndrew Gallatin 			       "tx_pkt_done",
16421e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.pkt_done,
16431e413cf9SAndrew Gallatin 			       0, "tx_done");
16441e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16451e413cf9SAndrew Gallatin 			       "tx_stall",
16461e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.stall,
16471e413cf9SAndrew Gallatin 			       0, "tx_stall");
16481e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16491e413cf9SAndrew Gallatin 			       "tx_wake",
16501e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.wake,
16511e413cf9SAndrew Gallatin 			       0, "tx_wake");
16521e413cf9SAndrew Gallatin 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16531e413cf9SAndrew Gallatin 			       "tx_defrag",
16541e413cf9SAndrew Gallatin 			       CTLFLAG_RD, &ss->tx.defrag,
16551e413cf9SAndrew Gallatin 			       0, "tx_defrag");
16561e413cf9SAndrew Gallatin 	}
1657b2fc195eSAndrew Gallatin }
1658b2fc195eSAndrew Gallatin 
1659b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1660b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1661b2fc195eSAndrew Gallatin 
1662b2fc195eSAndrew Gallatin static inline void
16631e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx,
1664b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1665b2fc195eSAndrew Gallatin {
1666b2fc195eSAndrew Gallatin         int idx, starting_slot;
1667b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1668b2fc195eSAndrew Gallatin         while (cnt > 1) {
1669b2fc195eSAndrew Gallatin                 cnt--;
1670b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
16716d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1672b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1673b2fc195eSAndrew Gallatin                 mb();
1674b2fc195eSAndrew Gallatin         }
1675b2fc195eSAndrew Gallatin }
1676b2fc195eSAndrew Gallatin 
1677b2fc195eSAndrew Gallatin /*
1678b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1679b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1680b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1681b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1682b2fc195eSAndrew Gallatin  */
1683b2fc195eSAndrew Gallatin 
1684b2fc195eSAndrew Gallatin static inline void
16851e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
1686b2fc195eSAndrew Gallatin                   int cnt)
1687b2fc195eSAndrew Gallatin {
1688b2fc195eSAndrew Gallatin         int idx, i;
1689b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1690b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1691b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1692b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
16935e7d8541SAndrew Gallatin 	uint8_t last_flags;
1694b2fc195eSAndrew Gallatin 
1695b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1696b2fc195eSAndrew Gallatin 
16975e7d8541SAndrew Gallatin 	last_flags = src->flags;
16985e7d8541SAndrew Gallatin 	src->flags = 0;
1699b2fc195eSAndrew Gallatin         mb();
1700b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1701b2fc195eSAndrew Gallatin         srcp = src;
1702b2fc195eSAndrew Gallatin 
1703b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1704b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
17056d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1706b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1707b2fc195eSAndrew Gallatin                         srcp += 2;
1708b2fc195eSAndrew Gallatin                         dstp += 2;
1709b2fc195eSAndrew Gallatin                 }
1710b2fc195eSAndrew Gallatin         } else {
1711b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1712b2fc195eSAndrew Gallatin                    that it is submitted below */
17136d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1714b2fc195eSAndrew Gallatin                 i = 0;
1715b2fc195eSAndrew Gallatin         }
1716b2fc195eSAndrew Gallatin         if (i < cnt) {
1717b2fc195eSAndrew Gallatin                 /* submit the first request */
17186d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1719b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1720b2fc195eSAndrew Gallatin         }
1721b2fc195eSAndrew Gallatin 
1722b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
17235e7d8541SAndrew Gallatin         src->flags = last_flags;
1724b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1725b2fc195eSAndrew Gallatin         src_ints+=3;
1726b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1727b2fc195eSAndrew Gallatin         dst_ints+=3;
1728b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1729b2fc195eSAndrew Gallatin         tx->req += cnt;
1730b2fc195eSAndrew Gallatin         mb();
1731b2fc195eSAndrew Gallatin }
1732b2fc195eSAndrew Gallatin 
173337d89b0cSAndrew Gallatin #if IFCAP_TSO4
173437d89b0cSAndrew Gallatin 
1735b2fc195eSAndrew Gallatin static void
17361e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17371e413cf9SAndrew Gallatin 	       int busdma_seg_cnt, int ip_off)
1738aed8e389SAndrew Gallatin {
17391e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1740aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1741aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1742aed8e389SAndrew Gallatin 	struct ip *ip;
1743aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1744aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1745aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1746aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1747aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1748aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1749aed8e389SAndrew Gallatin 	static int once;
1750aed8e389SAndrew Gallatin 
1751aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1752aed8e389SAndrew Gallatin 
1753aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1754aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1755aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1756aed8e389SAndrew Gallatin 	 */
1757aed8e389SAndrew Gallatin 
1758aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1759aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1760aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1761c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
1762c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + sizeof (*ip),
17631e413cf9SAndrew Gallatin 			   ss->scratch);
17641e413cf9SAndrew Gallatin 		ip = (struct ip *)(ss->scratch + ip_off);
1765aed8e389SAndrew Gallatin 	} else {
1766c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1767aed8e389SAndrew Gallatin 	}
1768c792928fSAndrew Gallatin 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
1769aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1770c792928fSAndrew Gallatin 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
17711e413cf9SAndrew Gallatin 			   + sizeof (*tcp),  ss->scratch);
1772c792928fSAndrew Gallatin 		ip = (struct ip *)(mtod(m, char *) + ip_off);
1773aed8e389SAndrew Gallatin 	}
1774aed8e389SAndrew Gallatin 
1775aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1776c792928fSAndrew Gallatin 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
1777aed8e389SAndrew Gallatin 
1778aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1779c792928fSAndrew Gallatin 	cksum_offset = ip_off + (ip->ip_hl << 2);
1780aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1781aed8e389SAndrew Gallatin 
1782aed8e389SAndrew Gallatin 
1783aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1784aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1785aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1786aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1787aed8e389SAndrew Gallatin 
17881e413cf9SAndrew Gallatin 	tx = &ss->tx;
1789aed8e389SAndrew Gallatin 	req = tx->req_list;
1790aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1791aed8e389SAndrew Gallatin 	cnt = 0;
1792aed8e389SAndrew Gallatin 	rdma_count = 0;
1793aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1794aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1795aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1796aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1797aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1798aed8e389SAndrew Gallatin 	 *
1799aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1800aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1801aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1802aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1803aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1804aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1805aed8e389SAndrew Gallatin 	 *
1806aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1807aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1808aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1809aed8e389SAndrew Gallatin 	 */
1810aed8e389SAndrew Gallatin 
1811aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1812aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1813aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1814aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1815e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1816aed8e389SAndrew Gallatin 
1817aed8e389SAndrew Gallatin 		while (len) {
1818aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1819e39a0a37SAndrew Gallatin 			seglen = len;
1820aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1821aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1822aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1823aed8e389SAndrew Gallatin 				/* payload */
1824aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1825aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1826aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1827aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1828aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1829aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1830aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1831aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1832aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1833aed8e389SAndrew Gallatin 				/* header ends */
1834aed8e389SAndrew Gallatin 				rdma_count = -1;
1835aed8e389SAndrew Gallatin 				cum_len_next = 0;
1836aed8e389SAndrew Gallatin 				seglen = -cum_len;
1837aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1838aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1839aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1840aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1841aed8e389SAndrew Gallatin 			    }
1842aed8e389SAndrew Gallatin 
1843aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1844aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1845aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1846aed8e389SAndrew Gallatin 			req->pad = 0;
1847aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1848aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1849aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1850aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1851aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1852aed8e389SAndrew Gallatin 			low += seglen;
1853aed8e389SAndrew Gallatin 			len -= seglen;
1854aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1855aed8e389SAndrew Gallatin 			flags = flags_next;
1856aed8e389SAndrew Gallatin 			req++;
1857aed8e389SAndrew Gallatin 			cnt++;
1858aed8e389SAndrew Gallatin 			rdma_count++;
1859aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1860aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1861aed8e389SAndrew Gallatin 			else
1862aed8e389SAndrew Gallatin 				cksum_offset = 0;
1863adae7080SAndrew Gallatin 			if (__predict_false(cnt > tx->max_desc))
1864aed8e389SAndrew Gallatin 				goto drop;
1865aed8e389SAndrew Gallatin 		}
1866aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1867aed8e389SAndrew Gallatin 		seg++;
1868aed8e389SAndrew Gallatin 	}
1869aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1870aed8e389SAndrew Gallatin 
1871aed8e389SAndrew Gallatin 	do {
1872aed8e389SAndrew Gallatin 		req--;
1873aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1874aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1875aed8e389SAndrew Gallatin 
1876aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1877aed8e389SAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
1878aed8e389SAndrew Gallatin 	return;
1879aed8e389SAndrew Gallatin 
1880aed8e389SAndrew Gallatin drop:
1881e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1882aed8e389SAndrew Gallatin 	m_freem(m);
18831e413cf9SAndrew Gallatin 	ss->sc->ifp->if_oerrors++;
1884aed8e389SAndrew Gallatin 	if (!once) {
1885adae7080SAndrew Gallatin 		printf("tx->max_desc exceeded via TSO!\n");
1886adae7080SAndrew Gallatin 		printf("mss = %d, %ld, %d!\n", mss,
1887adae7080SAndrew Gallatin 		       (long)seg - (long)tx->seg_list, tx->max_desc);
1888aed8e389SAndrew Gallatin 		once = 1;
1889aed8e389SAndrew Gallatin 	}
1890aed8e389SAndrew Gallatin 	return;
1891aed8e389SAndrew Gallatin 
1892aed8e389SAndrew Gallatin }
1893aed8e389SAndrew Gallatin 
189437d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */
189537d89b0cSAndrew Gallatin 
189637d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1897c792928fSAndrew Gallatin /*
1898c792928fSAndrew Gallatin  * We reproduce the software vlan tag insertion from
1899c792928fSAndrew Gallatin  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
1900c792928fSAndrew Gallatin  * vlan tag insertion. We need to advertise this in order to have the
1901c792928fSAndrew Gallatin  * vlan interface respect our csum offload flags.
1902c792928fSAndrew Gallatin  */
1903c792928fSAndrew Gallatin static struct mbuf *
1904c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m)
1905c792928fSAndrew Gallatin {
1906c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
1907c792928fSAndrew Gallatin 
1908c792928fSAndrew Gallatin 	M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
1909c792928fSAndrew Gallatin 	if (__predict_false(m == NULL))
1910c792928fSAndrew Gallatin 		return NULL;
1911c792928fSAndrew Gallatin 	if (m->m_len < sizeof(*evl)) {
1912c792928fSAndrew Gallatin 		m = m_pullup(m, sizeof(*evl));
1913c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1914c792928fSAndrew Gallatin 			return NULL;
1915c792928fSAndrew Gallatin 	}
1916c792928fSAndrew Gallatin 	/*
1917c792928fSAndrew Gallatin 	 * Transform the Ethernet header into an Ethernet header
1918c792928fSAndrew Gallatin 	 * with 802.1Q encapsulation.
1919c792928fSAndrew Gallatin 	 */
1920c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
1921c792928fSAndrew Gallatin 	bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN,
1922c792928fSAndrew Gallatin 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
1923c792928fSAndrew Gallatin 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1924c792928fSAndrew Gallatin 	evl->evl_tag = htons(m->m_pkthdr.ether_vtag);
1925c792928fSAndrew Gallatin 	m->m_flags &= ~M_VLANTAG;
1926c792928fSAndrew Gallatin 	return m;
1927c792928fSAndrew Gallatin }
192837d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */
1929c792928fSAndrew Gallatin 
1930aed8e389SAndrew Gallatin static void
19311e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
1932b2fc195eSAndrew Gallatin {
19331e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
1934b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1935b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1936b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1937b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
19381e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
1939b2fc195eSAndrew Gallatin 	struct ip *ip;
1940c792928fSAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
1941aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1942aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1943b2fc195eSAndrew Gallatin 
1944b2fc195eSAndrew Gallatin 
19451e413cf9SAndrew Gallatin 	sc = ss->sc;
1946b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
19471e413cf9SAndrew Gallatin 	tx = &ss->tx;
1948b2fc195eSAndrew Gallatin 
1949c792928fSAndrew Gallatin 	ip_off = sizeof (struct ether_header);
195037d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
1951c792928fSAndrew Gallatin 	if (m->m_flags & M_VLANTAG) {
1952c792928fSAndrew Gallatin 		m = mxge_vlan_tag_insert(m);
1953c792928fSAndrew Gallatin 		if (__predict_false(m == NULL))
1954c792928fSAndrew Gallatin 			goto drop;
1955c792928fSAndrew Gallatin 		ip_off += ETHER_VLAN_ENCAP_LEN;
1956c792928fSAndrew Gallatin 	}
195737d89b0cSAndrew Gallatin #endif
1958b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1959b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1960b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1961aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1962b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1963adae7080SAndrew Gallatin 	if (__predict_false(err == EFBIG)) {
1964b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1965b2fc195eSAndrew Gallatin 		   to defrag */
1966b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1967b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1968b2fc195eSAndrew Gallatin 			goto drop;
1969b2fc195eSAndrew Gallatin 		}
19701e413cf9SAndrew Gallatin 		ss->tx.defrag++;
1971b2fc195eSAndrew Gallatin 		m = m_tmp;
1972b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1973b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1974aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1975b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1976b2fc195eSAndrew Gallatin 	}
1977adae7080SAndrew Gallatin 	if (__predict_false(err != 0)) {
1978aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1979aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1980b2fc195eSAndrew Gallatin 		goto drop;
1981b2fc195eSAndrew Gallatin 	}
1982b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1983b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
19845e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1985b2fc195eSAndrew Gallatin 
198637d89b0cSAndrew Gallatin #if IFCAP_TSO4
1987aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1988aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
19891e413cf9SAndrew Gallatin 		mxge_encap_tso(ss, m, cnt, ip_off);
1990aed8e389SAndrew Gallatin 		return;
1991aed8e389SAndrew Gallatin 	}
199237d89b0cSAndrew Gallatin #endif
1993aed8e389SAndrew Gallatin 
1994b2fc195eSAndrew Gallatin 	req = tx->req_list;
1995b2fc195eSAndrew Gallatin 	cksum_offset = 0;
19965e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
19975e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1998b2fc195eSAndrew Gallatin 
1999b2fc195eSAndrew Gallatin 	/* checksum offloading? */
2000b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
2001aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
2002aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
2003c792928fSAndrew Gallatin 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
2004c792928fSAndrew Gallatin 			m_copydata(m, 0, ip_off + sizeof (*ip),
20051e413cf9SAndrew Gallatin 				   ss->scratch);
20061e413cf9SAndrew Gallatin 			ip = (struct ip *)(ss->scratch + ip_off);
2007aed8e389SAndrew Gallatin 		} else {
2008c792928fSAndrew Gallatin 			ip = (struct ip *)(mtod(m, char *) + ip_off);
2009aed8e389SAndrew Gallatin 		}
2010c792928fSAndrew Gallatin 		cksum_offset = ip_off + (ip->ip_hl << 2);
2011b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20125e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
2013b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
20145e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
2015aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
2016aed8e389SAndrew Gallatin 	} else {
2017aed8e389SAndrew Gallatin 		odd_flag = 0;
2018b2fc195eSAndrew Gallatin 	}
20195e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20205e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
2021b2fc195eSAndrew Gallatin 
2022b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
2023b2fc195eSAndrew Gallatin 	cum_len = 0;
2024aed8e389SAndrew Gallatin 	seg = tx->seg_list;
20255e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
2026b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
2027b2fc195eSAndrew Gallatin 		req->addr_low =
20286d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2029b2fc195eSAndrew Gallatin 		req->addr_high =
20306d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2031b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
2032b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
2033b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
2034b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
2035b2fc195eSAndrew Gallatin 		else
2036b2fc195eSAndrew Gallatin 			cksum_offset = 0;
20375e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20385e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20395e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2040aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2041b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
2042b2fc195eSAndrew Gallatin 		seg++;
2043b2fc195eSAndrew Gallatin 		req++;
2044b2fc195eSAndrew Gallatin 		req->flags = 0;
2045b2fc195eSAndrew Gallatin 	}
2046b2fc195eSAndrew Gallatin 	req--;
2047b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
2048b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
2049b2fc195eSAndrew Gallatin 		req++;
2050b2fc195eSAndrew Gallatin 		req->addr_low =
20516d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
2052b2fc195eSAndrew Gallatin 		req->addr_high =
20536d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
2054b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
20555e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
20565e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20575e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
20585e7d8541SAndrew Gallatin 		req->rdma_count = 1;
2059aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
2060b2fc195eSAndrew Gallatin 		cnt++;
2061b2fc195eSAndrew Gallatin 	}
20625e7d8541SAndrew Gallatin 
20635e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
20645e7d8541SAndrew Gallatin #if 0
20655e7d8541SAndrew Gallatin 	/* print what the firmware will see */
20665e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
20675e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
20685e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
20695e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
20705e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
20715e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
20725e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
20735e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
20745e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
20755e7d8541SAndrew Gallatin 	}
20765e7d8541SAndrew Gallatin 	printf("--------------\n");
20775e7d8541SAndrew Gallatin #endif
20785e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
20796d87a65dSAndrew Gallatin 	mxge_submit_req(tx, tx->req_list, cnt);
2080b2fc195eSAndrew Gallatin 	return;
2081b2fc195eSAndrew Gallatin 
2082b2fc195eSAndrew Gallatin drop:
2083b2fc195eSAndrew Gallatin 	m_freem(m);
2084b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
2085b2fc195eSAndrew Gallatin 	return;
2086b2fc195eSAndrew Gallatin }
2087b2fc195eSAndrew Gallatin 
2088b2fc195eSAndrew Gallatin 
20896d914a32SAndrew Gallatin 
20906d914a32SAndrew Gallatin 
20916d914a32SAndrew Gallatin static inline void
20921e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss)
2093b2fc195eSAndrew Gallatin {
20941e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2095b2fc195eSAndrew Gallatin 	struct mbuf *m;
2096b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
20971e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2098b2fc195eSAndrew Gallatin 
20991e413cf9SAndrew Gallatin 	sc = ss->sc;
2100b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
21011e413cf9SAndrew Gallatin 	tx = &ss->tx;
2102adae7080SAndrew Gallatin 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
21036d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
21046d914a32SAndrew Gallatin 		if (m == NULL) {
21056d914a32SAndrew Gallatin 			return;
21066d914a32SAndrew Gallatin 		}
2107b2fc195eSAndrew Gallatin 		/* let BPF see it */
2108b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
2109b2fc195eSAndrew Gallatin 
2110b2fc195eSAndrew Gallatin 		/* give it to the nic */
21111e413cf9SAndrew Gallatin 		mxge_encap(ss, m);
21126d914a32SAndrew Gallatin 	}
21136d914a32SAndrew Gallatin 	/* ran out of transmit slots */
2114a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2115b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
2116adae7080SAndrew Gallatin 		tx->stall++;
2117a82c2581SAndrew Gallatin 	}
2118b2fc195eSAndrew Gallatin }
2119b2fc195eSAndrew Gallatin 
2120b2fc195eSAndrew Gallatin static void
21216d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
2122b2fc195eSAndrew Gallatin {
21236d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
21241e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
2125b2fc195eSAndrew Gallatin 
21261e413cf9SAndrew Gallatin 	/* only use the first slice for now */
21271e413cf9SAndrew Gallatin 	ss = &sc->ss[0];
21281e413cf9SAndrew Gallatin 	mtx_lock(&ss->tx.mtx);
21291e413cf9SAndrew Gallatin 	mxge_start_locked(ss);
21301e413cf9SAndrew Gallatin 	mtx_unlock(&ss->tx.mtx);
2131b2fc195eSAndrew Gallatin }
2132b2fc195eSAndrew Gallatin 
21335e7d8541SAndrew Gallatin /*
21345e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
21355e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
21365e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
21375e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
21385e7d8541SAndrew Gallatin  * in a burst
21395e7d8541SAndrew Gallatin  */
21405e7d8541SAndrew Gallatin static inline void
21415e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
21425e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
21435e7d8541SAndrew Gallatin {
21445e7d8541SAndrew Gallatin 	uint32_t low;
21455e7d8541SAndrew Gallatin 
21465e7d8541SAndrew Gallatin 	low = src->addr_low;
21475e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
2148a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
2149a1480dfbSAndrew Gallatin 	mb();
2150a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
21515e7d8541SAndrew Gallatin 	mb();
215240385a5fSAndrew Gallatin 	src->addr_low = low;
21535e7d8541SAndrew Gallatin 	dst->addr_low = low;
21545e7d8541SAndrew Gallatin 	mb();
21555e7d8541SAndrew Gallatin }
21565e7d8541SAndrew Gallatin 
2157b2fc195eSAndrew Gallatin static int
21581e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2159b2fc195eSAndrew Gallatin {
2160b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
2161b2fc195eSAndrew Gallatin 	struct mbuf *m;
21621e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_small;
2163b2fc195eSAndrew Gallatin 	int cnt, err;
2164b2fc195eSAndrew Gallatin 
2165b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
2166b2fc195eSAndrew Gallatin 	if (m == NULL) {
2167b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2168b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2169b2fc195eSAndrew Gallatin 		goto done;
2170b2fc195eSAndrew Gallatin 	}
2171b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
2172b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2173b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
2174b2fc195eSAndrew Gallatin 	if (err != 0) {
2175b2fc195eSAndrew Gallatin 		m_free(m);
2176b2fc195eSAndrew Gallatin 		goto done;
2177b2fc195eSAndrew Gallatin 	}
2178b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2179b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
21806d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2181b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
21826d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
2183b2fc195eSAndrew Gallatin 
2184b2fc195eSAndrew Gallatin done:
2185adae7080SAndrew Gallatin 	if ((idx & 7) == 7)
2186adae7080SAndrew Gallatin 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
2187b2fc195eSAndrew Gallatin 	return err;
2188b2fc195eSAndrew Gallatin }
2189b2fc195eSAndrew Gallatin 
2190b2fc195eSAndrew Gallatin static int
21911e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
2192b2fc195eSAndrew Gallatin {
2193053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
2194b2fc195eSAndrew Gallatin 	struct mbuf *m;
21951e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx = &ss->rx_big;
2196053e637fSAndrew Gallatin 	int cnt, err, i;
2197b2fc195eSAndrew Gallatin 
2198a0394e33SAndrew Gallatin 	if (rx->cl_size == MCLBYTES)
2199a0394e33SAndrew Gallatin 		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
2200a0394e33SAndrew Gallatin 	else
2201053e637fSAndrew Gallatin 		m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
2202b2fc195eSAndrew Gallatin 	if (m == NULL) {
2203b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
2204b2fc195eSAndrew Gallatin 		err = ENOBUFS;
2205b2fc195eSAndrew Gallatin 		goto done;
2206b2fc195eSAndrew Gallatin 	}
2207053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
2208b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
2209053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
2210b2fc195eSAndrew Gallatin 	if (err != 0) {
2211b2fc195eSAndrew Gallatin 		m_free(m);
2212b2fc195eSAndrew Gallatin 		goto done;
2213b2fc195eSAndrew Gallatin 	}
2214b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
2215b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_low =
2216b0f7b922SAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
2217b0f7b922SAndrew Gallatin 	rx->shadow[idx].addr_high =
2218b0f7b922SAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
2219053e637fSAndrew Gallatin 
2220b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2221b0f7b922SAndrew Gallatin 	for (i = 1; i < cnt; i++) {
2222053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
2223053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
2224053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
2225053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
2226053e637fSAndrew Gallatin        }
2227b0f7b922SAndrew Gallatin #endif
2228b2fc195eSAndrew Gallatin 
2229b2fc195eSAndrew Gallatin done:
2230053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
2231b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
22325e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
22335e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
2234b2fc195eSAndrew Gallatin 		}
2235053e637fSAndrew Gallatin 		idx++;
2236053e637fSAndrew Gallatin 	}
2237b2fc195eSAndrew Gallatin 	return err;
2238b2fc195eSAndrew Gallatin }
2239b2fc195eSAndrew Gallatin 
22409b03b0f3SAndrew Gallatin /*
22419b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
22429b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
22439b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
22449b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
2245053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
2246053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
22479b03b0f3SAndrew Gallatin  */
22489b03b0f3SAndrew Gallatin 
2249053e637fSAndrew Gallatin static inline uint16_t
2250053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
2251053e637fSAndrew Gallatin {
2252053e637fSAndrew Gallatin 	struct ether_header *eh;
2253053e637fSAndrew Gallatin 	struct ip *ip;
2254053e637fSAndrew Gallatin 	uint16_t c;
2255053e637fSAndrew Gallatin 
2256053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2257053e637fSAndrew Gallatin 
2258053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
2259053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
2260053e637fSAndrew Gallatin 		return 1;
2261053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
2262053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
2263053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
2264053e637fSAndrew Gallatin 		return 1;
2265053e637fSAndrew Gallatin 
2266053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
2267053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
2268053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
2269053e637fSAndrew Gallatin 	c ^= 0xffff;
2270053e637fSAndrew Gallatin 	return (c);
22715e7d8541SAndrew Gallatin }
2272053e637fSAndrew Gallatin 
2273c792928fSAndrew Gallatin static void
2274c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
2275c792928fSAndrew Gallatin {
2276c792928fSAndrew Gallatin 	struct ether_vlan_header *evl;
2277c792928fSAndrew Gallatin 	struct ether_header *eh;
2278c792928fSAndrew Gallatin 	uint32_t partial;
2279c792928fSAndrew Gallatin 
2280c792928fSAndrew Gallatin 	evl = mtod(m, struct ether_vlan_header *);
2281c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2282c792928fSAndrew Gallatin 
2283c792928fSAndrew Gallatin 	/*
2284c792928fSAndrew Gallatin 	 * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes
2285c792928fSAndrew Gallatin 	 * after what the firmware thought was the end of the ethernet
2286c792928fSAndrew Gallatin 	 * header.
2287c792928fSAndrew Gallatin 	 */
2288c792928fSAndrew Gallatin 
2289c792928fSAndrew Gallatin 	/* put checksum into host byte order */
2290c792928fSAndrew Gallatin 	*csum = ntohs(*csum);
2291c792928fSAndrew Gallatin 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
2292c792928fSAndrew Gallatin 	(*csum) += ~partial;
2293c792928fSAndrew Gallatin 	(*csum) +=  ((*csum) < ~partial);
2294c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2295c792928fSAndrew Gallatin 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
2296c792928fSAndrew Gallatin 
2297c792928fSAndrew Gallatin 	/* restore checksum to network byte order;
2298c792928fSAndrew Gallatin 	   later consumers expect this */
2299c792928fSAndrew Gallatin 	*csum = htons(*csum);
2300c792928fSAndrew Gallatin 
2301c792928fSAndrew Gallatin 	/* save the tag */
230237d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
2303c792928fSAndrew Gallatin 	m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
230437d89b0cSAndrew Gallatin #else
230537d89b0cSAndrew Gallatin 	{
230637d89b0cSAndrew Gallatin 		struct m_tag *mtag;
230737d89b0cSAndrew Gallatin 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
230837d89b0cSAndrew Gallatin 				   M_NOWAIT);
230937d89b0cSAndrew Gallatin 		if (mtag == NULL)
231037d89b0cSAndrew Gallatin 			return;
231137d89b0cSAndrew Gallatin 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
231237d89b0cSAndrew Gallatin 		m_tag_prepend(m, mtag);
231337d89b0cSAndrew Gallatin 	}
231437d89b0cSAndrew Gallatin 
231537d89b0cSAndrew Gallatin #endif
231637d89b0cSAndrew Gallatin 	m->m_flags |= M_VLANTAG;
2317c792928fSAndrew Gallatin 
2318c792928fSAndrew Gallatin 	/*
2319c792928fSAndrew Gallatin 	 * Remove the 802.1q header by copying the Ethernet
2320c792928fSAndrew Gallatin 	 * addresses over it and adjusting the beginning of
2321c792928fSAndrew Gallatin 	 * the data in the mbuf.  The encapsulated Ethernet
2322c792928fSAndrew Gallatin 	 * type field is already in place.
2323c792928fSAndrew Gallatin 	 */
2324c792928fSAndrew Gallatin 	bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN,
2325c792928fSAndrew Gallatin 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2326c792928fSAndrew Gallatin 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
2327c792928fSAndrew Gallatin }
2328c792928fSAndrew Gallatin 
23295e7d8541SAndrew Gallatin 
23305e7d8541SAndrew Gallatin static inline void
23311e413cf9SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2332b2fc195eSAndrew Gallatin {
23331e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2334b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2335053e637fSAndrew Gallatin 	struct mbuf *m;
2336c792928fSAndrew Gallatin 	struct ether_header *eh;
23371e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2338053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2339b2fc195eSAndrew Gallatin 	int idx;
2340053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2341b2fc195eSAndrew Gallatin 
23421e413cf9SAndrew Gallatin 	sc = ss->sc;
2343b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
23441e413cf9SAndrew Gallatin 	rx = &ss->rx_big;
2345b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2346053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2347b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2348b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2349b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
23501e413cf9SAndrew Gallatin 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
2351053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2352053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2353053e637fSAndrew Gallatin 		return;
2354b2fc195eSAndrew Gallatin 	}
2355053e637fSAndrew Gallatin 
2356b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2357b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2358b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2359b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2360b2fc195eSAndrew Gallatin 
2361b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2362b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2363b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2364b2fc195eSAndrew Gallatin 
2365053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2366053e637fSAndrew Gallatin 	 * aligned */
23675e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2368b2fc195eSAndrew Gallatin 
2369053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2370053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
23711e413cf9SAndrew Gallatin 	ss->ipackets++;
2372c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2373c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2374c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2375c792928fSAndrew Gallatin 	}
2376b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2377053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
23781e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2379b2fc195eSAndrew Gallatin 			return;
2380053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2381053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2382053e637fSAndrew Gallatin 		   checksum is good */
2383053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2384053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2385b2fc195eSAndrew Gallatin 	}
2386053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2387053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2388b2fc195eSAndrew Gallatin }
2389b2fc195eSAndrew Gallatin 
2390b2fc195eSAndrew Gallatin static inline void
23911e413cf9SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
2392b2fc195eSAndrew Gallatin {
23931e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
2394b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2395c792928fSAndrew Gallatin 	struct ether_header *eh;
2396b2fc195eSAndrew Gallatin 	struct mbuf *m;
23971e413cf9SAndrew Gallatin 	mxge_rx_ring_t *rx;
2398b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2399b2fc195eSAndrew Gallatin 	int idx;
2400053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2401b2fc195eSAndrew Gallatin 
24021e413cf9SAndrew Gallatin 	sc = ss->sc;
2403b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
24041e413cf9SAndrew Gallatin 	rx = &ss->rx_small;
2405b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2406b2fc195eSAndrew Gallatin 	rx->cnt++;
2407b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2408b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2409b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
24101e413cf9SAndrew Gallatin 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
2411b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2412b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2413b2fc195eSAndrew Gallatin 		return;
2414b2fc195eSAndrew Gallatin 	}
2415b2fc195eSAndrew Gallatin 
2416b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2417b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2418b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2419b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2420b2fc195eSAndrew Gallatin 
2421b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2422b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2423b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2424b2fc195eSAndrew Gallatin 
2425b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2426b2fc195eSAndrew Gallatin 	 * aligned */
24275e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2428b2fc195eSAndrew Gallatin 
24299b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
24309b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
24311e413cf9SAndrew Gallatin 	ss->ipackets++;
2432c792928fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
2433c792928fSAndrew Gallatin 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
2434c792928fSAndrew Gallatin 		mxge_vlan_tag_remove(m, &csum);
2435c792928fSAndrew Gallatin 	}
2436b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2437053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
24381e413cf9SAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
2439053e637fSAndrew Gallatin 			return;
2440053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2441053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2442053e637fSAndrew Gallatin 		   checksum is good */
2443053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2444053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2445053e637fSAndrew Gallatin 	}
2446b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2447b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2448b2fc195eSAndrew Gallatin }
2449b2fc195eSAndrew Gallatin 
2450b2fc195eSAndrew Gallatin static inline void
24511e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss)
24525e7d8541SAndrew Gallatin {
24531e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
2454053e637fSAndrew Gallatin 	struct lro_entry *lro;
24555e7d8541SAndrew Gallatin 	int limit = 0;
24565e7d8541SAndrew Gallatin 	uint16_t length;
24575e7d8541SAndrew Gallatin 	uint16_t checksum;
24585e7d8541SAndrew Gallatin 
24595e7d8541SAndrew Gallatin 
24605e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
24615e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
24625e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2463053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2464b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
24651e413cf9SAndrew Gallatin 			mxge_rx_done_small(ss, length, checksum);
24665e7d8541SAndrew Gallatin 		else
24671e413cf9SAndrew Gallatin 			mxge_rx_done_big(ss, length, checksum);
24685e7d8541SAndrew Gallatin 		rx_done->cnt++;
2469adae7080SAndrew Gallatin 		rx_done->idx = rx_done->cnt & rx_done->mask;
24705e7d8541SAndrew Gallatin 
24715e7d8541SAndrew Gallatin 		/* limit potential for livelock */
2472f616ebc7SAndrew Gallatin 		if (__predict_false(++limit > rx_done->mask / 2))
24735e7d8541SAndrew Gallatin 			break;
2474053e637fSAndrew Gallatin 	}
24751e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_active)) {
24761e413cf9SAndrew Gallatin 		lro = SLIST_FIRST(&ss->lro_active);
24771e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
24781e413cf9SAndrew Gallatin 		mxge_lro_flush(ss, lro);
24795e7d8541SAndrew Gallatin 	}
24805e7d8541SAndrew Gallatin }
24815e7d8541SAndrew Gallatin 
24825e7d8541SAndrew Gallatin 
24835e7d8541SAndrew Gallatin static inline void
24841e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
2485b2fc195eSAndrew Gallatin {
2486b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
24871e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx;
2488b2fc195eSAndrew Gallatin 	struct mbuf *m;
2489b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
2490f616ebc7SAndrew Gallatin 	int idx;
2491b2fc195eSAndrew Gallatin 
24921e413cf9SAndrew Gallatin 	tx = &ss->tx;
24931e413cf9SAndrew Gallatin 	ifp = ss->sc->ifp;
24945e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2495b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2496b2fc195eSAndrew Gallatin 		tx->done++;
2497b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2498b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2499b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2500b2fc195eSAndrew Gallatin 		if (m != NULL) {
2501b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2502b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2503b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2504b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2505b2fc195eSAndrew Gallatin 			m_freem(m);
2506b2fc195eSAndrew Gallatin 		}
25075e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
25085e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
25095e7d8541SAndrew Gallatin 			tx->pkt_done++;
25105e7d8541SAndrew Gallatin 		}
2511b2fc195eSAndrew Gallatin 	}
2512b2fc195eSAndrew Gallatin 
2513b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2514b2fc195eSAndrew Gallatin            its OK to send packets */
2515b2fc195eSAndrew Gallatin 
2516b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2517b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
25181e413cf9SAndrew Gallatin 		mtx_lock(&ss->tx.mtx);
2519b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
25201e413cf9SAndrew Gallatin 		ss->tx.wake++;
25211e413cf9SAndrew Gallatin 		mxge_start_locked(ss);
25221e413cf9SAndrew Gallatin 		mtx_unlock(&ss->tx.mtx);
2523b2fc195eSAndrew Gallatin 	}
2524b2fc195eSAndrew Gallatin }
2525b2fc195eSAndrew Gallatin 
2526c587e59fSAndrew Gallatin static struct mxge_media_type mxge_media_types[] =
2527c587e59fSAndrew Gallatin {
2528c587e59fSAndrew Gallatin 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
2529c587e59fSAndrew Gallatin 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
2530c587e59fSAndrew Gallatin 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2531c587e59fSAndrew Gallatin 	{0,		(1 << 5),	"10GBASE-ER"},
2532c587e59fSAndrew Gallatin 	{0,		(1 << 4),	"10GBASE-LRM"},
2533c587e59fSAndrew Gallatin 	{0,		(1 << 3),	"10GBASE-SW"},
2534c587e59fSAndrew Gallatin 	{0,		(1 << 2),	"10GBASE-LW"},
2535c587e59fSAndrew Gallatin 	{0,		(1 << 1),	"10GBASE-EW"},
2536c587e59fSAndrew Gallatin 	{0,		(1 << 0),	"Reserved"}
2537c587e59fSAndrew Gallatin };
2538c587e59fSAndrew Gallatin 
2539c587e59fSAndrew Gallatin static void
2540c587e59fSAndrew Gallatin mxge_set_media(mxge_softc_t *sc, int type)
2541c587e59fSAndrew Gallatin {
2542c587e59fSAndrew Gallatin 	sc->media_flags |= type;
2543c587e59fSAndrew Gallatin 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
2544c587e59fSAndrew Gallatin 	ifmedia_set(&sc->media, sc->media_flags);
2545c587e59fSAndrew Gallatin }
2546c587e59fSAndrew Gallatin 
2547c587e59fSAndrew Gallatin 
2548c587e59fSAndrew Gallatin /*
2549c587e59fSAndrew Gallatin  * Determine the media type for a NIC.  Some XFPs will identify
2550c587e59fSAndrew Gallatin  * themselves only when their link is up, so this is initiated via a
2551c587e59fSAndrew Gallatin  * link up interrupt.  However, this can potentially take up to
2552c587e59fSAndrew Gallatin  * several milliseconds, so it is run via the watchdog routine, rather
2553c587e59fSAndrew Gallatin  * than in the interrupt handler itself.   This need only be done
2554c587e59fSAndrew Gallatin  * once, not each time the link is up.
2555c587e59fSAndrew Gallatin  */
2556c587e59fSAndrew Gallatin static void
2557c587e59fSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc)
2558c587e59fSAndrew Gallatin {
2559c587e59fSAndrew Gallatin 	mxge_cmd_t cmd;
2560c587e59fSAndrew Gallatin 	char *ptr;
2561c587e59fSAndrew Gallatin 	int i, err, ms;
2562c587e59fSAndrew Gallatin 
2563c587e59fSAndrew Gallatin 	sc->need_media_probe = 0;
2564c587e59fSAndrew Gallatin 
2565c587e59fSAndrew Gallatin 	/* if we've already set a media type, we're done */
2566c587e59fSAndrew Gallatin 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
2567c587e59fSAndrew Gallatin 		return;
2568c587e59fSAndrew Gallatin 
2569c587e59fSAndrew Gallatin 	/*
2570c587e59fSAndrew Gallatin 	 * parse the product code to deterimine the interface type
2571c587e59fSAndrew Gallatin 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
2572c587e59fSAndrew Gallatin 	 * after the 3rd dash in the driver's cached copy of the
2573c587e59fSAndrew Gallatin 	 * EEPROM's product code string.
2574c587e59fSAndrew Gallatin 	 */
2575c587e59fSAndrew Gallatin 	ptr = sc->product_code_string;
2576c587e59fSAndrew Gallatin 	if (ptr == NULL) {
2577c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Missing product code\n");
2578c587e59fSAndrew Gallatin 	}
2579c587e59fSAndrew Gallatin 
2580c587e59fSAndrew Gallatin 	for (i = 0; i < 3; i++, ptr++) {
258137d89b0cSAndrew Gallatin 		ptr = index(ptr, '-');
2582c587e59fSAndrew Gallatin 		if (ptr == NULL) {
2583c587e59fSAndrew Gallatin 			device_printf(sc->dev,
2584c587e59fSAndrew Gallatin 				      "only %d dashes in PC?!?\n", i);
2585c587e59fSAndrew Gallatin 			return;
2586c587e59fSAndrew Gallatin 		}
2587c587e59fSAndrew Gallatin 	}
2588c587e59fSAndrew Gallatin 	if (*ptr == 'C') {
2589c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2590c587e59fSAndrew Gallatin 		return;
2591c587e59fSAndrew Gallatin 	}
2592c587e59fSAndrew Gallatin 	else if (*ptr == 'Q') {
2593c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
2594c587e59fSAndrew Gallatin 		/* FreeBSD has no media type for Quad ribbon fiber */
2595c587e59fSAndrew Gallatin 		return;
2596c587e59fSAndrew Gallatin 	}
2597c587e59fSAndrew Gallatin 
2598c587e59fSAndrew Gallatin 	if (*ptr != 'R') {
2599c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
2600c587e59fSAndrew Gallatin 		return;
2601c587e59fSAndrew Gallatin 	}
2602c587e59fSAndrew Gallatin 
2603c587e59fSAndrew Gallatin 	/*
2604c587e59fSAndrew Gallatin 	 * At this point we know the NIC has an XFP cage, so now we
2605c587e59fSAndrew Gallatin 	 * try to determine what is in the cage by using the
2606c587e59fSAndrew Gallatin 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
2607c587e59fSAndrew Gallatin 	 * register.  We read just one byte, which may take over
2608c587e59fSAndrew Gallatin 	 * a millisecond
2609c587e59fSAndrew Gallatin 	 */
2610c587e59fSAndrew Gallatin 
2611c587e59fSAndrew Gallatin 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
2612c587e59fSAndrew Gallatin 	cmd.data1 = MXGE_XFP_COMPLIANCE_BYTE; /* the byte we want */
2613c587e59fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_I2C_READ, &cmd);
2614c587e59fSAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_XFP_FAILURE) {
2615c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP\n");
2616c587e59fSAndrew Gallatin 	}
2617c587e59fSAndrew Gallatin 	if (err == MXGEFW_CMD_ERROR_XFP_ABSENT) {
2618c587e59fSAndrew Gallatin 		device_printf(sc->dev, "Type R with no XFP!?!?\n");
2619c587e59fSAndrew Gallatin 	}
2620c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2621c587e59fSAndrew Gallatin 		return;
2622c587e59fSAndrew Gallatin 	}
2623c587e59fSAndrew Gallatin 
2624c587e59fSAndrew Gallatin 	/* now we wait for the data to be cached */
2625c587e59fSAndrew Gallatin 	cmd.data0 = MXGE_XFP_COMPLIANCE_BYTE;
2626c587e59fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_BYTE, &cmd);
2627c587e59fSAndrew Gallatin 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
2628c587e59fSAndrew Gallatin 		DELAY(1000);
2629c587e59fSAndrew Gallatin 		cmd.data0 = MXGE_XFP_COMPLIANCE_BYTE;
2630c587e59fSAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_XFP_BYTE, &cmd);
2631c587e59fSAndrew Gallatin 	}
2632c587e59fSAndrew Gallatin 	if (err != MXGEFW_CMD_OK) {
2633c587e59fSAndrew Gallatin 		device_printf(sc->dev, "failed to read XFP (%d, %dms)\n",
2634c587e59fSAndrew Gallatin 			      err, ms);
2635c587e59fSAndrew Gallatin 		return;
2636c587e59fSAndrew Gallatin 	}
2637c587e59fSAndrew Gallatin 
2638c587e59fSAndrew Gallatin 	if (cmd.data0 == mxge_media_types[0].bitmask) {
2639c587e59fSAndrew Gallatin 		if (mxge_verbose)
2640c587e59fSAndrew Gallatin 			device_printf(sc->dev, "XFP:%s\n",
2641c587e59fSAndrew Gallatin 				      mxge_media_types[0].name);
2642c587e59fSAndrew Gallatin 		mxge_set_media(sc, IFM_10G_CX4);
2643c587e59fSAndrew Gallatin 		return;
2644c587e59fSAndrew Gallatin 	}
2645c587e59fSAndrew Gallatin 	for (i = 1;
2646c587e59fSAndrew Gallatin 	     i < sizeof (mxge_media_types) / sizeof (mxge_media_types[0]);
2647c587e59fSAndrew Gallatin 	     i++) {
2648c587e59fSAndrew Gallatin 		if (cmd.data0 & mxge_media_types[i].bitmask) {
2649c587e59fSAndrew Gallatin 			if (mxge_verbose)
2650c587e59fSAndrew Gallatin 				device_printf(sc->dev, "XFP:%s\n",
2651c587e59fSAndrew Gallatin 					      mxge_media_types[i].name);
2652c587e59fSAndrew Gallatin 
2653c587e59fSAndrew Gallatin 			mxge_set_media(sc, mxge_media_types[i].flag);
2654c587e59fSAndrew Gallatin 			return;
2655c587e59fSAndrew Gallatin 		}
2656c587e59fSAndrew Gallatin 	}
2657c587e59fSAndrew Gallatin 	device_printf(sc->dev, "XFP media 0x%x unknown\n", cmd.data0);
2658c587e59fSAndrew Gallatin 
2659c587e59fSAndrew Gallatin 	return;
2660c587e59fSAndrew Gallatin }
2661c587e59fSAndrew Gallatin 
2662b2fc195eSAndrew Gallatin static void
26636d87a65dSAndrew Gallatin mxge_intr(void *arg)
2664b2fc195eSAndrew Gallatin {
26651e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss = arg;
26661e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
26671e413cf9SAndrew Gallatin 	mcp_irq_data_t *stats = ss->fw_stats;
26681e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &ss->tx;
26691e413cf9SAndrew Gallatin 	mxge_rx_done_t *rx_done = &ss->rx_done;
26705e7d8541SAndrew Gallatin 	uint32_t send_done_count;
26715e7d8541SAndrew Gallatin 	uint8_t valid;
2672b2fc195eSAndrew Gallatin 
2673b2fc195eSAndrew Gallatin 
26741e413cf9SAndrew Gallatin 	/* an interrupt on a non-zero slice is implicitly valid
26751e413cf9SAndrew Gallatin 	   since MSI-X irqs are not shared */
26761e413cf9SAndrew Gallatin 	if (ss != sc->ss) {
26771e413cf9SAndrew Gallatin 		mxge_clean_rx_done(ss);
26781e413cf9SAndrew Gallatin 		*ss->irq_claim = be32toh(3);
26791e413cf9SAndrew Gallatin 		return;
26801e413cf9SAndrew Gallatin 	}
26811e413cf9SAndrew Gallatin 
26825e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
26835e7d8541SAndrew Gallatin 	if (!stats->valid) {
26845e7d8541SAndrew Gallatin 		return;
2685b2fc195eSAndrew Gallatin 	}
26865e7d8541SAndrew Gallatin 	valid = stats->valid;
2687b2fc195eSAndrew Gallatin 
268891ed8913SAndrew Gallatin 	if (sc->legacy_irq) {
26895e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
26905e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
26915e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
26925e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
26935e7d8541SAndrew Gallatin 			stats->valid = 0;
2694dc8731d4SAndrew Gallatin 	} else {
2695dc8731d4SAndrew Gallatin 		stats->valid = 0;
2696dc8731d4SAndrew Gallatin 	}
2697dc8731d4SAndrew Gallatin 
2698dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
26995e7d8541SAndrew Gallatin 	do {
27005e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
27015e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
27025e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
27035e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
27041e413cf9SAndrew Gallatin 			mxge_tx_done(ss, (int)send_done_count);
27051e413cf9SAndrew Gallatin 			mxge_clean_rx_done(ss);
27065e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2707b2fc195eSAndrew Gallatin 		}
270891ed8913SAndrew Gallatin 		if (sc->legacy_irq && mxge_deassert_wait)
270991ed8913SAndrew Gallatin 			mb();
27105e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2711b2fc195eSAndrew Gallatin 
27125e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
27135e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
27145e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2715b2fc195eSAndrew Gallatin 			if (sc->link_state) {
27165e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
27175e7d8541SAndrew Gallatin 				if (mxge_verbose)
27185e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2719b2fc195eSAndrew Gallatin 			} else {
27205e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
27215e7d8541SAndrew Gallatin 				if (mxge_verbose)
27225e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2723b2fc195eSAndrew Gallatin 			}
2724c587e59fSAndrew Gallatin 			sc->need_media_probe = 1;
2725b2fc195eSAndrew Gallatin 		}
2726b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
27271e413cf9SAndrew Gallatin 		    be32toh(stats->rdma_tags_available)) {
2728b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
27291e413cf9SAndrew Gallatin 				be32toh(stats->rdma_tags_available);
27305e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
27315e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
27325e7d8541SAndrew Gallatin 		}
2733c587e59fSAndrew Gallatin 
2734c587e59fSAndrew Gallatin 		if (stats->link_down) {
27355e7d8541SAndrew Gallatin 			sc->down_cnt += stats->link_down;
2736c587e59fSAndrew Gallatin 			sc->link_state = 0;
2737c587e59fSAndrew Gallatin 			if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2738c587e59fSAndrew Gallatin 		}
2739b2fc195eSAndrew Gallatin 	}
2740b2fc195eSAndrew Gallatin 
27415e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
27425e7d8541SAndrew Gallatin 	if (valid & 0x1)
27431e413cf9SAndrew Gallatin 	    *ss->irq_claim = be32toh(3);
27441e413cf9SAndrew Gallatin 	*(ss->irq_claim + 1) = be32toh(3);
2745b2fc195eSAndrew Gallatin }
2746b2fc195eSAndrew Gallatin 
2747b2fc195eSAndrew Gallatin static void
27486d87a65dSAndrew Gallatin mxge_init(void *arg)
2749b2fc195eSAndrew Gallatin {
2750b2fc195eSAndrew Gallatin }
2751b2fc195eSAndrew Gallatin 
2752b2fc195eSAndrew Gallatin 
2753b2fc195eSAndrew Gallatin 
2754b2fc195eSAndrew Gallatin static void
27551e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss)
27561e413cf9SAndrew Gallatin {
27571e413cf9SAndrew Gallatin 	struct lro_entry *lro_entry;
27581e413cf9SAndrew Gallatin 	int i;
27591e413cf9SAndrew Gallatin 
27601e413cf9SAndrew Gallatin 	while (!SLIST_EMPTY(&ss->lro_free)) {
27611e413cf9SAndrew Gallatin 		lro_entry = SLIST_FIRST(&ss->lro_free);
27621e413cf9SAndrew Gallatin 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
27631e413cf9SAndrew Gallatin 		free(lro_entry, M_DEVBUF);
27641e413cf9SAndrew Gallatin 	}
27651e413cf9SAndrew Gallatin 
27661e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
27671e413cf9SAndrew Gallatin 		if (ss->rx_big.info[i].m == NULL)
27681e413cf9SAndrew Gallatin 			continue;
27691e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_big.dmat,
27701e413cf9SAndrew Gallatin 				  ss->rx_big.info[i].map);
27711e413cf9SAndrew Gallatin 		m_freem(ss->rx_big.info[i].m);
27721e413cf9SAndrew Gallatin 		ss->rx_big.info[i].m = NULL;
27731e413cf9SAndrew Gallatin 	}
27741e413cf9SAndrew Gallatin 
27751e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
27761e413cf9SAndrew Gallatin 		if (ss->rx_small.info[i].m == NULL)
27771e413cf9SAndrew Gallatin 			continue;
27781e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->rx_small.dmat,
27791e413cf9SAndrew Gallatin 				  ss->rx_small.info[i].map);
27801e413cf9SAndrew Gallatin 		m_freem(ss->rx_small.info[i].m);
27811e413cf9SAndrew Gallatin 		ss->rx_small.info[i].m = NULL;
27821e413cf9SAndrew Gallatin 	}
27831e413cf9SAndrew Gallatin 
27841e413cf9SAndrew Gallatin 	/* transmit ring used only on the first slice */
27851e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
27861e413cf9SAndrew Gallatin 		return;
27871e413cf9SAndrew Gallatin 
27881e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
27891e413cf9SAndrew Gallatin 		ss->tx.info[i].flag = 0;
27901e413cf9SAndrew Gallatin 		if (ss->tx.info[i].m == NULL)
27911e413cf9SAndrew Gallatin 			continue;
27921e413cf9SAndrew Gallatin 		bus_dmamap_unload(ss->tx.dmat,
27931e413cf9SAndrew Gallatin 				  ss->tx.info[i].map);
27941e413cf9SAndrew Gallatin 		m_freem(ss->tx.info[i].m);
27951e413cf9SAndrew Gallatin 		ss->tx.info[i].m = NULL;
27961e413cf9SAndrew Gallatin 	}
27971e413cf9SAndrew Gallatin }
27981e413cf9SAndrew Gallatin 
27991e413cf9SAndrew Gallatin static void
28006d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2801b2fc195eSAndrew Gallatin {
28021e413cf9SAndrew Gallatin 	int slice;
28031e413cf9SAndrew Gallatin 
28041e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
28051e413cf9SAndrew Gallatin 		mxge_free_slice_mbufs(&sc->ss[slice]);
28061e413cf9SAndrew Gallatin }
28071e413cf9SAndrew Gallatin 
28081e413cf9SAndrew Gallatin static void
28091e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss)
28101e413cf9SAndrew Gallatin {
2811b2fc195eSAndrew Gallatin 	int i;
2812b2fc195eSAndrew Gallatin 
2813b2fc195eSAndrew Gallatin 
28141e413cf9SAndrew Gallatin 	if (ss->rx_done.entry != NULL)
28151e413cf9SAndrew Gallatin 		mxge_dma_free(&ss->rx_done.dma);
28161e413cf9SAndrew Gallatin 	ss->rx_done.entry = NULL;
2817b2fc195eSAndrew Gallatin 
28181e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes != NULL)
28191e413cf9SAndrew Gallatin 		free(ss->tx.req_bytes, M_DEVBUF);
28201e413cf9SAndrew Gallatin 	ss->tx.req_bytes = NULL;
28211e413cf9SAndrew Gallatin 
28221e413cf9SAndrew Gallatin 	if (ss->tx.seg_list != NULL)
28231e413cf9SAndrew Gallatin 		free(ss->tx.seg_list, M_DEVBUF);
28241e413cf9SAndrew Gallatin 	ss->tx.seg_list = NULL;
28251e413cf9SAndrew Gallatin 
28261e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow != NULL)
28271e413cf9SAndrew Gallatin 		free(ss->rx_small.shadow, M_DEVBUF);
28281e413cf9SAndrew Gallatin 	ss->rx_small.shadow = NULL;
28291e413cf9SAndrew Gallatin 
28301e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow != NULL)
28311e413cf9SAndrew Gallatin 		free(ss->rx_big.shadow, M_DEVBUF);
28321e413cf9SAndrew Gallatin 	ss->rx_big.shadow = NULL;
28331e413cf9SAndrew Gallatin 
28341e413cf9SAndrew Gallatin 	if (ss->tx.info != NULL) {
28351e413cf9SAndrew Gallatin 		if (ss->tx.dmat != NULL) {
28361e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->tx.mask; i++) {
28371e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->tx.dmat,
28381e413cf9SAndrew Gallatin 						   ss->tx.info[i].map);
2839b2fc195eSAndrew Gallatin 			}
28401e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->tx.dmat);
28411e413cf9SAndrew Gallatin 		}
28421e413cf9SAndrew Gallatin 		free(ss->tx.info, M_DEVBUF);
28431e413cf9SAndrew Gallatin 	}
28441e413cf9SAndrew Gallatin 	ss->tx.info = NULL;
28451e413cf9SAndrew Gallatin 
28461e413cf9SAndrew Gallatin 	if (ss->rx_small.info != NULL) {
28471e413cf9SAndrew Gallatin 		if (ss->rx_small.dmat != NULL) {
28481e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_small.mask; i++) {
28491e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_small.dmat,
28501e413cf9SAndrew Gallatin 						   ss->rx_small.info[i].map);
28511e413cf9SAndrew Gallatin 			}
28521e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_small.dmat,
28531e413cf9SAndrew Gallatin 					   ss->rx_small.extra_map);
28541e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_small.dmat);
28551e413cf9SAndrew Gallatin 		}
28561e413cf9SAndrew Gallatin 		free(ss->rx_small.info, M_DEVBUF);
28571e413cf9SAndrew Gallatin 	}
28581e413cf9SAndrew Gallatin 	ss->rx_small.info = NULL;
28591e413cf9SAndrew Gallatin 
28601e413cf9SAndrew Gallatin 	if (ss->rx_big.info != NULL) {
28611e413cf9SAndrew Gallatin 		if (ss->rx_big.dmat != NULL) {
28621e413cf9SAndrew Gallatin 			for (i = 0; i <= ss->rx_big.mask; i++) {
28631e413cf9SAndrew Gallatin 				bus_dmamap_destroy(ss->rx_big.dmat,
28641e413cf9SAndrew Gallatin 						   ss->rx_big.info[i].map);
28651e413cf9SAndrew Gallatin 			}
28661e413cf9SAndrew Gallatin 			bus_dmamap_destroy(ss->rx_big.dmat,
28671e413cf9SAndrew Gallatin 					   ss->rx_big.extra_map);
28681e413cf9SAndrew Gallatin 			bus_dma_tag_destroy(ss->rx_big.dmat);
28691e413cf9SAndrew Gallatin 		}
28701e413cf9SAndrew Gallatin 		free(ss->rx_big.info, M_DEVBUF);
28711e413cf9SAndrew Gallatin 	}
28721e413cf9SAndrew Gallatin 	ss->rx_big.info = NULL;
2873b2fc195eSAndrew Gallatin }
2874b2fc195eSAndrew Gallatin 
2875b2fc195eSAndrew Gallatin static void
28766d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2877b2fc195eSAndrew Gallatin {
28781e413cf9SAndrew Gallatin 	int slice;
2879b2fc195eSAndrew Gallatin 
28801e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++)
28811e413cf9SAndrew Gallatin 		mxge_free_slice_rings(&sc->ss[slice]);
2882c2657176SAndrew Gallatin }
2883b2fc195eSAndrew Gallatin 
2884b2fc195eSAndrew Gallatin static int
28851e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28861e413cf9SAndrew Gallatin 		       int tx_ring_entries)
2887b2fc195eSAndrew Gallatin {
28881e413cf9SAndrew Gallatin 	mxge_softc_t *sc = ss->sc;
28891e413cf9SAndrew Gallatin 	size_t bytes;
28901e413cf9SAndrew Gallatin 	int err, i;
2891b2fc195eSAndrew Gallatin 
2892b2fc195eSAndrew Gallatin 	err = ENOMEM;
2893b2fc195eSAndrew Gallatin 
28941e413cf9SAndrew Gallatin 	/* allocate per-slice receive resources */
2895adae7080SAndrew Gallatin 
28961e413cf9SAndrew Gallatin 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
28971e413cf9SAndrew Gallatin 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
2898aed8e389SAndrew Gallatin 
2899b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
29001e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
29011e413cf9SAndrew Gallatin 	ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29021e413cf9SAndrew Gallatin 	if (ss->rx_small.shadow == NULL)
29031e413cf9SAndrew Gallatin 		return err;;
2904b2fc195eSAndrew Gallatin 
29051e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
29061e413cf9SAndrew Gallatin 	ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29071e413cf9SAndrew Gallatin 	if (ss->rx_big.shadow == NULL)
29081e413cf9SAndrew Gallatin 		return err;;
2909b2fc195eSAndrew Gallatin 
29101e413cf9SAndrew Gallatin 	/* allocate the rx host info rings */
29111e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
29121e413cf9SAndrew Gallatin 	ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29131e413cf9SAndrew Gallatin 	if (ss->rx_small.info == NULL)
29141e413cf9SAndrew Gallatin 		return err;;
2915b2fc195eSAndrew Gallatin 
29161e413cf9SAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
29171e413cf9SAndrew Gallatin 	ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29181e413cf9SAndrew Gallatin 	if (ss->rx_big.info == NULL)
29191e413cf9SAndrew Gallatin 		return err;;
2920b2fc195eSAndrew Gallatin 
29211e413cf9SAndrew Gallatin 	/* allocate the rx busdma resources */
2922b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2923b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2924b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2925b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2926b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2927b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2928b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2929b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2930b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2931b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2932b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29331e413cf9SAndrew Gallatin 				 &ss->rx_small.dmat);	/* tag */
2934b2fc195eSAndrew Gallatin 	if (err != 0) {
2935b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2936b2fc195eSAndrew Gallatin 			      err);
29371e413cf9SAndrew Gallatin 		return err;;
2938b2fc195eSAndrew Gallatin 	}
2939b2fc195eSAndrew Gallatin 
2940b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2941b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2942b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2943b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2944b0f7b922SAndrew Gallatin #else
2945b0f7b922SAndrew Gallatin 				 0,			/* boundary */
2946b0f7b922SAndrew Gallatin #endif
2947b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2948b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2949b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2950053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2951b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
2952053e637fSAndrew Gallatin 				 3,			/* num segs */
2953b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize*/
2954b0f7b922SAndrew Gallatin #else
2955b0f7b922SAndrew Gallatin 				 1,			/* num segs */
2956b0f7b922SAndrew Gallatin 				 MJUM9BYTES,		/* maxsegsize*/
2957b0f7b922SAndrew Gallatin #endif
2958b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2959b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
29601e413cf9SAndrew Gallatin 				 &ss->rx_big.dmat);	/* tag */
2961b2fc195eSAndrew Gallatin 	if (err != 0) {
2962b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2963b2fc195eSAndrew Gallatin 			      err);
29641e413cf9SAndrew Gallatin 		return err;;
2965b2fc195eSAndrew Gallatin 	}
29661e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
29671e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
29681e413cf9SAndrew Gallatin 					&ss->rx_small.info[i].map);
2969b2fc195eSAndrew Gallatin 		if (err != 0) {
2970b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2971b2fc195eSAndrew Gallatin 				      err);
29721e413cf9SAndrew Gallatin 			return err;;
2973b2fc195eSAndrew Gallatin 		}
2974b2fc195eSAndrew Gallatin 	}
29751e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
29761e413cf9SAndrew Gallatin 				&ss->rx_small.extra_map);
2977b2fc195eSAndrew Gallatin 	if (err != 0) {
2978b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2979b2fc195eSAndrew Gallatin 			      err);
29801e413cf9SAndrew Gallatin 		return err;;
2981b2fc195eSAndrew Gallatin 	}
2982b2fc195eSAndrew Gallatin 
29831e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
29841e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
29851e413cf9SAndrew Gallatin 					&ss->rx_big.info[i].map);
2986b2fc195eSAndrew Gallatin 		if (err != 0) {
2987b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2988b2fc195eSAndrew Gallatin 				      err);
29891e413cf9SAndrew Gallatin 			return err;;
2990b2fc195eSAndrew Gallatin 		}
2991b2fc195eSAndrew Gallatin 	}
29921e413cf9SAndrew Gallatin 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
29931e413cf9SAndrew Gallatin 				&ss->rx_big.extra_map);
2994b2fc195eSAndrew Gallatin 	if (err != 0) {
2995b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2996b2fc195eSAndrew Gallatin 			      err);
29971e413cf9SAndrew Gallatin 		return err;;
29981e413cf9SAndrew Gallatin 	}
29991e413cf9SAndrew Gallatin 
30001e413cf9SAndrew Gallatin 	/* now allocate TX resouces */
30011e413cf9SAndrew Gallatin 
30021e413cf9SAndrew Gallatin 	/* only use a single TX ring for now */
30031e413cf9SAndrew Gallatin 	if (ss != ss->sc->ss)
30041e413cf9SAndrew Gallatin 		return 0;
30051e413cf9SAndrew Gallatin 
30061e413cf9SAndrew Gallatin 	ss->tx.mask = tx_ring_entries - 1;
30071e413cf9SAndrew Gallatin 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
30081e413cf9SAndrew Gallatin 
30091e413cf9SAndrew Gallatin 
30101e413cf9SAndrew Gallatin 	/* allocate the tx request copy block */
30111e413cf9SAndrew Gallatin 	bytes = 8 +
30121e413cf9SAndrew Gallatin 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
30131e413cf9SAndrew Gallatin 	ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
30141e413cf9SAndrew Gallatin 	if (ss->tx.req_bytes == NULL)
30151e413cf9SAndrew Gallatin 		return err;;
30161e413cf9SAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
30171e413cf9SAndrew Gallatin 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
30181e413cf9SAndrew Gallatin 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
30191e413cf9SAndrew Gallatin 
30201e413cf9SAndrew Gallatin 	/* allocate the tx busdma segment list */
30211e413cf9SAndrew Gallatin 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
30221e413cf9SAndrew Gallatin 	ss->tx.seg_list = (bus_dma_segment_t *)
30231e413cf9SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
30241e413cf9SAndrew Gallatin 	if (ss->tx.seg_list == NULL)
30251e413cf9SAndrew Gallatin 		return err;;
30261e413cf9SAndrew Gallatin 
30271e413cf9SAndrew Gallatin 	/* allocate the tx host info ring */
30281e413cf9SAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
30291e413cf9SAndrew Gallatin 	ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30301e413cf9SAndrew Gallatin 	if (ss->tx.info == NULL)
30311e413cf9SAndrew Gallatin 		return err;;
30321e413cf9SAndrew Gallatin 
30331e413cf9SAndrew Gallatin 	/* allocate the tx busdma resources */
30341e413cf9SAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30351e413cf9SAndrew Gallatin 				 1,			/* alignment */
30361e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* boundary */
30371e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
30381e413cf9SAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
30391e413cf9SAndrew Gallatin 				 NULL, NULL,		/* filter */
30401e413cf9SAndrew Gallatin 				 65536 + 256,		/* maxsize */
30411e413cf9SAndrew Gallatin 				 ss->tx.max_desc - 2,	/* num segs */
30421e413cf9SAndrew Gallatin 				 sc->tx_boundary,	/* maxsegsz */
30431e413cf9SAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
30441e413cf9SAndrew Gallatin 				 NULL, NULL,		/* lock */
30451e413cf9SAndrew Gallatin 				 &ss->tx.dmat);		/* tag */
30461e413cf9SAndrew Gallatin 
30471e413cf9SAndrew Gallatin 	if (err != 0) {
30481e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
30491e413cf9SAndrew Gallatin 			      err);
30501e413cf9SAndrew Gallatin 		return err;;
30511e413cf9SAndrew Gallatin 	}
30521e413cf9SAndrew Gallatin 
30531e413cf9SAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
30541e413cf9SAndrew Gallatin 	   in the ring */
30551e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->tx.mask; i++) {
30561e413cf9SAndrew Gallatin 		err = bus_dmamap_create(ss->tx.dmat, 0,
30571e413cf9SAndrew Gallatin 					&ss->tx.info[i].map);
30581e413cf9SAndrew Gallatin 		if (err != 0) {
30591e413cf9SAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
30601e413cf9SAndrew Gallatin 				      err);
30611e413cf9SAndrew Gallatin 			return err;;
30621e413cf9SAndrew Gallatin 		}
3063b2fc195eSAndrew Gallatin 	}
3064b2fc195eSAndrew Gallatin 	return 0;
3065b2fc195eSAndrew Gallatin 
3066b2fc195eSAndrew Gallatin }
3067b2fc195eSAndrew Gallatin 
30681e413cf9SAndrew Gallatin static int
30691e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
30701e413cf9SAndrew Gallatin {
30711e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
30721e413cf9SAndrew Gallatin 	int tx_ring_size;
30731e413cf9SAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
30741e413cf9SAndrew Gallatin 	int err, slice;
30751e413cf9SAndrew Gallatin 
30761e413cf9SAndrew Gallatin 	/* get ring sizes */
30771e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30781e413cf9SAndrew Gallatin 	tx_ring_size = cmd.data0;
30791e413cf9SAndrew Gallatin 	if (err != 0) {
30801e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
30811e413cf9SAndrew Gallatin 		goto abort;
30821e413cf9SAndrew Gallatin 	}
30831e413cf9SAndrew Gallatin 
30841e413cf9SAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
30851e413cf9SAndrew Gallatin 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
30861e413cf9SAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
30871e413cf9SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
30881e413cf9SAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
30891e413cf9SAndrew Gallatin 
30901e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
30911e413cf9SAndrew Gallatin 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30921e413cf9SAndrew Gallatin 					     rx_ring_entries,
30931e413cf9SAndrew Gallatin 					     tx_ring_entries);
30941e413cf9SAndrew Gallatin 		if (err != 0)
30951e413cf9SAndrew Gallatin 			goto abort;
30961e413cf9SAndrew Gallatin 	}
30971e413cf9SAndrew Gallatin 	return 0;
30981e413cf9SAndrew Gallatin 
30991e413cf9SAndrew Gallatin abort:
31001e413cf9SAndrew Gallatin 	mxge_free_rings(sc);
31011e413cf9SAndrew Gallatin 	return err;
31021e413cf9SAndrew Gallatin 
31031e413cf9SAndrew Gallatin }
31041e413cf9SAndrew Gallatin 
31051e413cf9SAndrew Gallatin 
3106053e637fSAndrew Gallatin static void
3107053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
3108053e637fSAndrew Gallatin {
3109c792928fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD;
3110053e637fSAndrew Gallatin 
3111053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
3112053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
3113053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
3114053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
3115053e637fSAndrew Gallatin 		*nbufs = 1;
3116053e637fSAndrew Gallatin 		return;
3117053e637fSAndrew Gallatin 	}
3118053e637fSAndrew Gallatin 
3119053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
3120053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
3121053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
3122053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
3123053e637fSAndrew Gallatin 		*nbufs = 1;
3124053e637fSAndrew Gallatin 		return;
3125053e637fSAndrew Gallatin 	}
3126b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS
3127053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
3128053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
3129053e637fSAndrew Gallatin 	*big_buf_size = 4096;
3130053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
3131053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
3132053e637fSAndrew Gallatin 	if (*nbufs == 3)
3133053e637fSAndrew Gallatin 		*nbufs = 4;
3134b0f7b922SAndrew Gallatin #else
3135b0f7b922SAndrew Gallatin 	*cl_size = MJUM9BYTES;
3136b0f7b922SAndrew Gallatin 	*big_buf_size = MJUM9BYTES;
3137b0f7b922SAndrew Gallatin 	*nbufs = 1;
3138b0f7b922SAndrew Gallatin #endif
3139053e637fSAndrew Gallatin }
3140053e637fSAndrew Gallatin 
3141b2fc195eSAndrew Gallatin static int
31421e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
3143b2fc195eSAndrew Gallatin {
31441e413cf9SAndrew Gallatin 	mxge_softc_t *sc;
31456d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3146b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
3147053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
31481e413cf9SAndrew Gallatin 	int err, i, slice;
3149b2fc195eSAndrew Gallatin 
31501e413cf9SAndrew Gallatin 
31511e413cf9SAndrew Gallatin 	sc = ss->sc;
31521e413cf9SAndrew Gallatin 	slice = ss - sc->ss;
31531e413cf9SAndrew Gallatin 
31541e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_free);
31551e413cf9SAndrew Gallatin 	SLIST_INIT(&ss->lro_active);
3156053e637fSAndrew Gallatin 
3157053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
3158053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
31591e413cf9SAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF,
31601e413cf9SAndrew Gallatin 			       M_NOWAIT | M_ZERO);
3161053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
3162053e637fSAndrew Gallatin 			sc->lro_cnt = i;
3163053e637fSAndrew Gallatin 			break;
3164053e637fSAndrew Gallatin 		}
31651e413cf9SAndrew Gallatin 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
3166053e637fSAndrew Gallatin 	}
31671e413cf9SAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
31681e413cf9SAndrew Gallatin 
31691e413cf9SAndrew Gallatin 	err = 0;
31701e413cf9SAndrew Gallatin 	/* We currently only send from the first slice */
31711e413cf9SAndrew Gallatin 	if (slice == 0) {
31721e413cf9SAndrew Gallatin 		cmd.data0 = slice;
31731e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
31741e413cf9SAndrew Gallatin 		ss->tx.lanai =
31751e413cf9SAndrew Gallatin 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
31761e413cf9SAndrew Gallatin 	}
31771e413cf9SAndrew Gallatin 	cmd.data0 = slice;
31781e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc,
31791e413cf9SAndrew Gallatin 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
31801e413cf9SAndrew Gallatin 	ss->rx_small.lanai =
31811e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
31821e413cf9SAndrew Gallatin 	cmd.data0 = slice;
31831e413cf9SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
31841e413cf9SAndrew Gallatin 	ss->rx_big.lanai =
31851e413cf9SAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
31861e413cf9SAndrew Gallatin 
31871e413cf9SAndrew Gallatin 	if (err != 0) {
31881e413cf9SAndrew Gallatin 		device_printf(sc->dev,
31891e413cf9SAndrew Gallatin 			      "failed to get ring sizes or locations\n");
31901e413cf9SAndrew Gallatin 		return EIO;
31911e413cf9SAndrew Gallatin 	}
31921e413cf9SAndrew Gallatin 
31931e413cf9SAndrew Gallatin 	/* stock receive rings */
31941e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_small.mask; i++) {
31951e413cf9SAndrew Gallatin 		map = ss->rx_small.info[i].map;
31961e413cf9SAndrew Gallatin 		err = mxge_get_buf_small(ss, map, i);
31971e413cf9SAndrew Gallatin 		if (err) {
31981e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
31991e413cf9SAndrew Gallatin 				      i, ss->rx_small.mask + 1);
32001e413cf9SAndrew Gallatin 			return ENOMEM;
32011e413cf9SAndrew Gallatin 		}
32021e413cf9SAndrew Gallatin 	}
32031e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i++) {
32041e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
32051e413cf9SAndrew Gallatin 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
32061e413cf9SAndrew Gallatin 	}
32071e413cf9SAndrew Gallatin 	ss->rx_big.nbufs = nbufs;
32081e413cf9SAndrew Gallatin 	ss->rx_big.cl_size = cl_size;
32091e413cf9SAndrew Gallatin 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
32101e413cf9SAndrew Gallatin 		map = ss->rx_big.info[i].map;
32111e413cf9SAndrew Gallatin 		err = mxge_get_buf_big(ss, map, i);
32121e413cf9SAndrew Gallatin 		if (err) {
32131e413cf9SAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
32141e413cf9SAndrew Gallatin 				      i, ss->rx_big.mask + 1);
32151e413cf9SAndrew Gallatin 			return ENOMEM;
32161e413cf9SAndrew Gallatin 		}
32171e413cf9SAndrew Gallatin 	}
32181e413cf9SAndrew Gallatin 	return 0;
32191e413cf9SAndrew Gallatin }
32201e413cf9SAndrew Gallatin 
32211e413cf9SAndrew Gallatin static int
32221e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc)
32231e413cf9SAndrew Gallatin {
32241e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
32251e413cf9SAndrew Gallatin 	int err, big_bytes, nbufs, slice, cl_size, i;
32261e413cf9SAndrew Gallatin 	bus_addr_t bus;
32271e413cf9SAndrew Gallatin 	volatile uint8_t *itable;
3228b2fc195eSAndrew Gallatin 
32297d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
32307d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
32317d542e2dSAndrew Gallatin 
3232adae7080SAndrew Gallatin 	err = mxge_reset(sc, 1);
3233b2fc195eSAndrew Gallatin 	if (err != 0) {
3234b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
3235b2fc195eSAndrew Gallatin 		return EIO;
3236b2fc195eSAndrew Gallatin 	}
3237b2fc195eSAndrew Gallatin 
32381e413cf9SAndrew Gallatin 	if (sc->num_slices > 1) {
32391e413cf9SAndrew Gallatin 		/* setup the indirection table */
32401e413cf9SAndrew Gallatin 		cmd.data0 = sc->num_slices;
32411e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
32421e413cf9SAndrew Gallatin 				    &cmd);
3243b2fc195eSAndrew Gallatin 
32441e413cf9SAndrew Gallatin 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
32451e413cf9SAndrew Gallatin 				     &cmd);
32461e413cf9SAndrew Gallatin 		if (err != 0) {
32471e413cf9SAndrew Gallatin 			device_printf(sc->dev,
32481e413cf9SAndrew Gallatin 				      "failed to setup rss tables\n");
32491e413cf9SAndrew Gallatin 			return err;
32501e413cf9SAndrew Gallatin 		}
32511e413cf9SAndrew Gallatin 
32521e413cf9SAndrew Gallatin 		/* just enable an identity mapping */
32531e413cf9SAndrew Gallatin 		itable = sc->sram + cmd.data0;
32541e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
32551e413cf9SAndrew Gallatin 			itable[i] = (uint8_t)i;
32561e413cf9SAndrew Gallatin 
32571e413cf9SAndrew Gallatin 		cmd.data0 = 1;
32581e413cf9SAndrew Gallatin 		cmd.data1 = mxge_rss_hash_type;
32591e413cf9SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
32601e413cf9SAndrew Gallatin 		if (err != 0) {
32611e413cf9SAndrew Gallatin 			device_printf(sc->dev, "failed to enable slices\n");
32621e413cf9SAndrew Gallatin 			return err;
32631e413cf9SAndrew Gallatin 		}
32641e413cf9SAndrew Gallatin 	}
32651e413cf9SAndrew Gallatin 
32661e413cf9SAndrew Gallatin 
32671e413cf9SAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
32681e413cf9SAndrew Gallatin 
32691e413cf9SAndrew Gallatin 	cmd.data0 = nbufs;
3270053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
3271053e637fSAndrew Gallatin 			    &cmd);
3272053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
3273053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
32741e413cf9SAndrew Gallatin 	if (err && nbufs > 1) {
3275053e637fSAndrew Gallatin 		device_printf(sc->dev,
3276053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
32771e413cf9SAndrew Gallatin 			      nbufs);
3278053e637fSAndrew Gallatin 		return EIO;
3279053e637fSAndrew Gallatin 	}
3280b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
3281b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
3282b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
3283c792928fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
32845e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
3285b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
32865e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
3287b2fc195eSAndrew Gallatin 			     &cmd);
3288053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
32895e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32900fa7f681SAndrew Gallatin 
32910fa7f681SAndrew Gallatin 	if (err != 0) {
32920fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
32930fa7f681SAndrew Gallatin 		goto abort;
32940fa7f681SAndrew Gallatin 	}
32950fa7f681SAndrew Gallatin 
3296b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
32971e413cf9SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
32981e413cf9SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->ss->fw_stats_dma.bus_addr);
32990fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
33000fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
33010fa7f681SAndrew Gallatin 
33020fa7f681SAndrew Gallatin 	if (err != 0) {
33031e413cf9SAndrew Gallatin 		bus = sc->ss->fw_stats_dma.bus_addr;
33040fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
33050fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
33060fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
33070fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
33080fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
33090fa7f681SAndrew Gallatin 				    &cmd);
33100fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
33110fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
33120fa7f681SAndrew Gallatin 	} else {
33130fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
33140fa7f681SAndrew Gallatin 	}
3315b2fc195eSAndrew Gallatin 
3316b2fc195eSAndrew Gallatin 	if (err != 0) {
3317b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
3318b2fc195eSAndrew Gallatin 		goto abort;
3319b2fc195eSAndrew Gallatin 	}
3320b2fc195eSAndrew Gallatin 
33211e413cf9SAndrew Gallatin 	for (slice = 0; slice < sc->num_slices; slice++) {
33221e413cf9SAndrew Gallatin 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
33231e413cf9SAndrew Gallatin 		if (err != 0) {
33241e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't open slice %d\n",
33251e413cf9SAndrew Gallatin 				      slice);
33261e413cf9SAndrew Gallatin 			goto abort;
33271e413cf9SAndrew Gallatin 		}
33281e413cf9SAndrew Gallatin 	}
33291e413cf9SAndrew Gallatin 
3330b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
33315e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
3332b2fc195eSAndrew Gallatin 	if (err) {
3333b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
3334b2fc195eSAndrew Gallatin 		goto abort;
3335b2fc195eSAndrew Gallatin 	}
3336b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
3337b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
3338b2fc195eSAndrew Gallatin 
3339b2fc195eSAndrew Gallatin 	return 0;
3340b2fc195eSAndrew Gallatin 
3341b2fc195eSAndrew Gallatin 
3342b2fc195eSAndrew Gallatin abort:
33436d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3344a98d6cd7SAndrew Gallatin 
3345b2fc195eSAndrew Gallatin 	return err;
3346b2fc195eSAndrew Gallatin }
3347b2fc195eSAndrew Gallatin 
3348b2fc195eSAndrew Gallatin static int
33496d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
3350b2fc195eSAndrew Gallatin {
33516d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
3352b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
3353b2fc195eSAndrew Gallatin 
3354b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
3355b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
3356b2fc195eSAndrew Gallatin 	mb();
33575e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
3358b2fc195eSAndrew Gallatin 	if (err) {
3359b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
3360b2fc195eSAndrew Gallatin 	}
3361b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3362b2fc195eSAndrew Gallatin 		/* wait for down irq */
3363dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
3364b2fc195eSAndrew Gallatin 	}
33651e413cf9SAndrew Gallatin 	mb();
3366b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
3367b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
3368b2fc195eSAndrew Gallatin 	}
3369a98d6cd7SAndrew Gallatin 
33706d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
3371a98d6cd7SAndrew Gallatin 
3372b2fc195eSAndrew Gallatin 	return 0;
3373b2fc195eSAndrew Gallatin }
3374b2fc195eSAndrew Gallatin 
3375dce01b9bSAndrew Gallatin static void
3376dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
3377dce01b9bSAndrew Gallatin {
3378dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3379dce01b9bSAndrew Gallatin 	int reg;
3380dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
3381dce01b9bSAndrew Gallatin 
3382dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
3383dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
3384dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
3385dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
3386dce01b9bSAndrew Gallatin 
3387dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
3388dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
3389dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
3390dce01b9bSAndrew Gallatin 	}
3391dce01b9bSAndrew Gallatin 
3392dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
3393dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
3394dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
3395dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
3396dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
3397dce01b9bSAndrew Gallatin }
3398dce01b9bSAndrew Gallatin 
3399dce01b9bSAndrew Gallatin static uint32_t
3400dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
3401dce01b9bSAndrew Gallatin {
3402dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
3403dce01b9bSAndrew Gallatin 	uint32_t vs;
3404dce01b9bSAndrew Gallatin 
3405dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
3406dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
3407dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
3408dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
3409dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
3410dce01b9bSAndrew Gallatin 	}
3411dce01b9bSAndrew Gallatin 	/* enable read32 mode */
3412dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
3413dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
3414dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
3415dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
3416dce01b9bSAndrew Gallatin }
3417dce01b9bSAndrew Gallatin 
3418dce01b9bSAndrew Gallatin static void
3419dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
3420dce01b9bSAndrew Gallatin {
3421dce01b9bSAndrew Gallatin 	int err;
3422dce01b9bSAndrew Gallatin 	uint32_t reboot;
3423dce01b9bSAndrew Gallatin 	uint16_t cmd;
3424dce01b9bSAndrew Gallatin 
3425dce01b9bSAndrew Gallatin 	err = ENXIO;
3426dce01b9bSAndrew Gallatin 
3427dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
3428dce01b9bSAndrew Gallatin 
3429dce01b9bSAndrew Gallatin 	/*
3430dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
3431dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
3432dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
3433dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
3434dce01b9bSAndrew Gallatin 	 * again
3435dce01b9bSAndrew Gallatin 	 */
3436dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3437dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
3438dce01b9bSAndrew Gallatin 		/*
3439dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
3440dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
3441dce01b9bSAndrew Gallatin 		 * back, then give up
3442dce01b9bSAndrew Gallatin 		 */
3443dce01b9bSAndrew Gallatin 		DELAY(1000*100);
3444dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
3445dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
3446dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
3447dce01b9bSAndrew Gallatin 			goto abort;
3448dce01b9bSAndrew Gallatin 		}
3449dce01b9bSAndrew Gallatin 	}
3450dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
3451dce01b9bSAndrew Gallatin 		/* print the reboot status */
3452dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
3453dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
3454dce01b9bSAndrew Gallatin 			      reboot);
3455dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
3456dce01b9bSAndrew Gallatin 
3457dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
3458dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
3459dce01b9bSAndrew Gallatin 
3460dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
3461dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
346210882804SAndrew Gallatin 
346310882804SAndrew Gallatin 		if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
346410882804SAndrew Gallatin 			mxge_close(sc);
346510882804SAndrew Gallatin 			err = mxge_open(sc);
346610882804SAndrew Gallatin 		}
3467dce01b9bSAndrew Gallatin 	} else {
3468dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
3469dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
34701e413cf9SAndrew Gallatin 			      sc->ss->tx.req, sc->ss->tx.done);
3471dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
34721e413cf9SAndrew Gallatin 			      sc->ss->tx.pkt_done,
34731e413cf9SAndrew Gallatin 			      be32toh(sc->ss->fw_stats->send_done_count));
347410882804SAndrew Gallatin 		device_printf(sc->dev, "not resetting\n");
3475dce01b9bSAndrew Gallatin 	}
3476dce01b9bSAndrew Gallatin 
3477dce01b9bSAndrew Gallatin abort:
3478dce01b9bSAndrew Gallatin 	/*
3479dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
3480dce01b9bSAndrew Gallatin 	 * console
3481dce01b9bSAndrew Gallatin 	 */
3482dce01b9bSAndrew Gallatin 	if (err != 0) {
3483dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
3484dce01b9bSAndrew Gallatin 	}
3485dce01b9bSAndrew Gallatin }
3486dce01b9bSAndrew Gallatin 
3487dce01b9bSAndrew Gallatin static void
3488dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
3489dce01b9bSAndrew Gallatin {
34901e413cf9SAndrew Gallatin 	mxge_tx_ring_t *tx = &sc->ss->tx;
34911e413cf9SAndrew Gallatin 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
3492dce01b9bSAndrew Gallatin 
3493dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
3494dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
3495dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
3496dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
3497c587e59fSAndrew Gallatin 	    tx->done == tx->watchdog_done) {
3498c587e59fSAndrew Gallatin 		/* check for pause blocking before resetting */
3499c587e59fSAndrew Gallatin 		if (tx->watchdog_rx_pause == rx_pause)
3500dce01b9bSAndrew Gallatin 			mxge_watchdog_reset(sc);
3501c587e59fSAndrew Gallatin 		else
3502c587e59fSAndrew Gallatin 			device_printf(sc->dev, "Flow control blocking "
3503c587e59fSAndrew Gallatin 				      "xmits, check link partner\n");
3504c587e59fSAndrew Gallatin 	}
3505dce01b9bSAndrew Gallatin 
3506dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
3507dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
3508c587e59fSAndrew Gallatin 	tx->watchdog_rx_pause = rx_pause;
3509c587e59fSAndrew Gallatin 
3510c587e59fSAndrew Gallatin 	if (sc->need_media_probe)
3511c587e59fSAndrew Gallatin 		mxge_media_probe(sc);
3512dce01b9bSAndrew Gallatin }
3513dce01b9bSAndrew Gallatin 
3514dce01b9bSAndrew Gallatin static void
35151e413cf9SAndrew Gallatin mxge_update_stats(mxge_softc_t *sc)
35161e413cf9SAndrew Gallatin {
35171e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
35181e413cf9SAndrew Gallatin 	u_long ipackets = 0;
35191e413cf9SAndrew Gallatin 	int slice;
35201e413cf9SAndrew Gallatin 
35211e413cf9SAndrew Gallatin 	for(slice = 0; slice < sc->num_slices; slice++) {
35221e413cf9SAndrew Gallatin 		ss = &sc->ss[slice];
35231e413cf9SAndrew Gallatin 		ipackets += ss->ipackets;
35241e413cf9SAndrew Gallatin 	}
35251e413cf9SAndrew Gallatin 	sc->ifp->if_ipackets = ipackets;
35261e413cf9SAndrew Gallatin 
35271e413cf9SAndrew Gallatin }
35281e413cf9SAndrew Gallatin static void
3529dce01b9bSAndrew Gallatin mxge_tick(void *arg)
3530dce01b9bSAndrew Gallatin {
3531dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
3532dce01b9bSAndrew Gallatin 
3533dce01b9bSAndrew Gallatin 
3534dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
3535dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
3536dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
3537dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3538dce01b9bSAndrew Gallatin 		return;
3539dce01b9bSAndrew Gallatin 	}
3540dce01b9bSAndrew Gallatin 
35411e413cf9SAndrew Gallatin 	/* aggregate stats from different slices */
35421e413cf9SAndrew Gallatin 	mxge_update_stats(sc);
35431e413cf9SAndrew Gallatin 
3544dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
35451e413cf9SAndrew Gallatin 	if (!sc->watchdog_countdown) {
3546dce01b9bSAndrew Gallatin 		mxge_watchdog(sc);
35471e413cf9SAndrew Gallatin 		sc->watchdog_countdown = 4;
35481e413cf9SAndrew Gallatin 	}
35491e413cf9SAndrew Gallatin 	sc->watchdog_countdown--;
3550dce01b9bSAndrew Gallatin }
3551b2fc195eSAndrew Gallatin 
3552b2fc195eSAndrew Gallatin static int
35536d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
3554b2fc195eSAndrew Gallatin {
3555b2fc195eSAndrew Gallatin 	return EINVAL;
3556b2fc195eSAndrew Gallatin }
3557b2fc195eSAndrew Gallatin 
3558b2fc195eSAndrew Gallatin static int
35596d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
3560b2fc195eSAndrew Gallatin {
3561b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
3562b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
3563b2fc195eSAndrew Gallatin 	int err = 0;
3564b2fc195eSAndrew Gallatin 
3565b2fc195eSAndrew Gallatin 
3566c792928fSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
3567053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
3568b2fc195eSAndrew Gallatin 		return EINVAL;
3569a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3570b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
3571b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
3572b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3573dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
35746d87a65dSAndrew Gallatin 		mxge_close(sc);
35756d87a65dSAndrew Gallatin 		err = mxge_open(sc);
3576b2fc195eSAndrew Gallatin 		if (err != 0) {
3577b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
35786d87a65dSAndrew Gallatin 			mxge_close(sc);
35796d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
3580b2fc195eSAndrew Gallatin 		}
3581dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
3582b2fc195eSAndrew Gallatin 	}
3583a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3584b2fc195eSAndrew Gallatin 	return err;
3585b2fc195eSAndrew Gallatin }
3586b2fc195eSAndrew Gallatin 
3587b2fc195eSAndrew Gallatin static void
35886d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
3589b2fc195eSAndrew Gallatin {
35906d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3591b2fc195eSAndrew Gallatin 
3592b2fc195eSAndrew Gallatin 
3593b2fc195eSAndrew Gallatin 	if (sc == NULL)
3594b2fc195eSAndrew Gallatin 		return;
3595b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
3596c587e59fSAndrew Gallatin 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
3597b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
3598c587e59fSAndrew Gallatin 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
3599b2fc195eSAndrew Gallatin }
3600b2fc195eSAndrew Gallatin 
3601b2fc195eSAndrew Gallatin static int
36026d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
3603b2fc195eSAndrew Gallatin {
36046d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
3605b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
3606b2fc195eSAndrew Gallatin 	int err, mask;
3607b2fc195eSAndrew Gallatin 
3608b2fc195eSAndrew Gallatin 	err = 0;
3609b2fc195eSAndrew Gallatin 	switch (command) {
3610b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
3611b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
3612b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
3613b2fc195eSAndrew Gallatin 		break;
3614b2fc195eSAndrew Gallatin 
3615b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
36166d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
3617b2fc195eSAndrew Gallatin 		break;
3618b2fc195eSAndrew Gallatin 
3619b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
3620a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3621b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
3622dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
36236d87a65dSAndrew Gallatin 				err = mxge_open(sc);
3624dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
3625dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
3626dce01b9bSAndrew Gallatin 			} else {
36270fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
36280fa7f681SAndrew Gallatin 				   flag chages */
36290fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
36300fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
36310fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
36320fa7f681SAndrew Gallatin 			}
3633b2fc195eSAndrew Gallatin 		} else {
3634dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3635dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
36361e413cf9SAndrew Gallatin 				mxge_close(sc);
3637dce01b9bSAndrew Gallatin 			}
3638b2fc195eSAndrew Gallatin 		}
3639a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3640b2fc195eSAndrew Gallatin 		break;
3641b2fc195eSAndrew Gallatin 
3642b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
3643b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
3644a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
36450fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
3646a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3647b2fc195eSAndrew Gallatin 		break;
3648b2fc195eSAndrew Gallatin 
3649b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
3650a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
3651b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
3652b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
3653b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
3654aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
3655aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3656aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3657b2fc195eSAndrew Gallatin 			} else {
3658b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3659b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3660b2fc195eSAndrew Gallatin 			}
3661b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3662b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3663b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
36645e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3665b2fc195eSAndrew Gallatin 			} else {
3666b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
36675e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3668b2fc195eSAndrew Gallatin 			}
3669b2fc195eSAndrew Gallatin 		}
3670aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3671aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3672aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3673aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3674aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3675aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3676aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3677aed8e389SAndrew Gallatin 			} else {
3678aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3679aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3680aed8e389SAndrew Gallatin 				err = EINVAL;
3681aed8e389SAndrew Gallatin 			}
3682aed8e389SAndrew Gallatin 		}
3683f04b33f8SAndrew Gallatin 		if (mask & IFCAP_LRO) {
3684f04b33f8SAndrew Gallatin 			if (IFCAP_LRO & ifp->if_capenable)
3685f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, 0);
3686f04b33f8SAndrew Gallatin 			else
3687f04b33f8SAndrew Gallatin 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
3688f04b33f8SAndrew Gallatin 		}
3689c792928fSAndrew Gallatin 		if (mask & IFCAP_VLAN_HWTAGGING)
3690c792928fSAndrew Gallatin 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
3691a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3692c792928fSAndrew Gallatin 		VLAN_CAPABILITIES(ifp);
3693c792928fSAndrew Gallatin 
3694b2fc195eSAndrew Gallatin 		break;
3695b2fc195eSAndrew Gallatin 
3696b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3697b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3698b2fc195eSAndrew Gallatin 				    &sc->media, command);
3699b2fc195eSAndrew Gallatin                 break;
3700b2fc195eSAndrew Gallatin 
3701b2fc195eSAndrew Gallatin 	default:
3702b2fc195eSAndrew Gallatin 		err = ENOTTY;
3703b2fc195eSAndrew Gallatin         }
3704b2fc195eSAndrew Gallatin 	return err;
3705b2fc195eSAndrew Gallatin }
3706b2fc195eSAndrew Gallatin 
3707b2fc195eSAndrew Gallatin static void
37086d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3709b2fc195eSAndrew Gallatin {
3710b2fc195eSAndrew Gallatin 
37111e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
37126d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
37136d87a65dSAndrew Gallatin 			  &mxge_flow_control);
37146d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
37156d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
37166d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
37176d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3718d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3719d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
37205e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
37215e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
37225e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
37235e7d8541SAndrew Gallatin 			  &mxge_verbose);
3724dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3725053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
37261e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
37271e413cf9SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
3728f04b33f8SAndrew Gallatin 	if (sc->lro_cnt != 0)
3729f04b33f8SAndrew Gallatin 		mxge_lro_cnt = sc->lro_cnt;
3730b2fc195eSAndrew Gallatin 
37315e7d8541SAndrew Gallatin 	if (bootverbose)
37325e7d8541SAndrew Gallatin 		mxge_verbose = 1;
37336d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
37346d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3735dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
37361e413cf9SAndrew Gallatin 		mxge_ticks = hz / 2;
37376d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
37381e413cf9SAndrew Gallatin 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
37391e413cf9SAndrew Gallatin 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_SRC_PORT) {
37401e413cf9SAndrew Gallatin 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
3741b2fc195eSAndrew Gallatin 	}
37421e413cf9SAndrew Gallatin }
37431e413cf9SAndrew Gallatin 
37441e413cf9SAndrew Gallatin 
37451e413cf9SAndrew Gallatin static void
37461e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc)
37471e413cf9SAndrew Gallatin {
37481e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37491e413cf9SAndrew Gallatin 	int i;
37501e413cf9SAndrew Gallatin 
37511e413cf9SAndrew Gallatin 
37521e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
37531e413cf9SAndrew Gallatin 		return;
37541e413cf9SAndrew Gallatin 
37551e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
37561e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
37571e413cf9SAndrew Gallatin 		if (ss->fw_stats != NULL) {
37581e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->fw_stats_dma);
37591e413cf9SAndrew Gallatin 			ss->fw_stats = NULL;
37601e413cf9SAndrew Gallatin 			mtx_destroy(&ss->tx.mtx);
37611e413cf9SAndrew Gallatin 		}
37621e413cf9SAndrew Gallatin 		if (ss->rx_done.entry != NULL) {
37631e413cf9SAndrew Gallatin 			mxge_dma_free(&ss->rx_done.dma);
37641e413cf9SAndrew Gallatin 			ss->rx_done.entry = NULL;
37651e413cf9SAndrew Gallatin 		}
37661e413cf9SAndrew Gallatin 	}
37671e413cf9SAndrew Gallatin 	free(sc->ss, M_DEVBUF);
37681e413cf9SAndrew Gallatin 	sc->ss = NULL;
37691e413cf9SAndrew Gallatin }
37701e413cf9SAndrew Gallatin 
37711e413cf9SAndrew Gallatin static int
37721e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc)
37731e413cf9SAndrew Gallatin {
37741e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
37751e413cf9SAndrew Gallatin 	struct mxge_slice_state *ss;
37761e413cf9SAndrew Gallatin 	size_t bytes;
37771e413cf9SAndrew Gallatin 	int err, i, max_intr_slots;
37781e413cf9SAndrew Gallatin 
37791e413cf9SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37801e413cf9SAndrew Gallatin 	if (err != 0) {
37811e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37821e413cf9SAndrew Gallatin 		return err;
37831e413cf9SAndrew Gallatin 	}
37841e413cf9SAndrew Gallatin 	sc->rx_ring_size = cmd.data0;
37851e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37861e413cf9SAndrew Gallatin 
37871e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->ss) * sc->num_slices;
37881e413cf9SAndrew Gallatin 	sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
37891e413cf9SAndrew Gallatin 	if (sc->ss == NULL)
37901e413cf9SAndrew Gallatin 		return (ENOMEM);
37911e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
37921e413cf9SAndrew Gallatin 		ss = &sc->ss[i];
37931e413cf9SAndrew Gallatin 
37941e413cf9SAndrew Gallatin 		ss->sc = sc;
37951e413cf9SAndrew Gallatin 
37961e413cf9SAndrew Gallatin 		/* allocate per-slice rx interrupt queues */
37971e413cf9SAndrew Gallatin 
37981e413cf9SAndrew Gallatin 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
37991e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
38001e413cf9SAndrew Gallatin 		if (err != 0)
38011e413cf9SAndrew Gallatin 			goto abort;
38021e413cf9SAndrew Gallatin 		ss->rx_done.entry = ss->rx_done.dma.addr;
38031e413cf9SAndrew Gallatin 		bzero(ss->rx_done.entry, bytes);
38041e413cf9SAndrew Gallatin 
38051e413cf9SAndrew Gallatin 		/*
38061e413cf9SAndrew Gallatin 		 * allocate the per-slice firmware stats; stats
38071e413cf9SAndrew Gallatin 		 * (including tx) are used used only on the first
38081e413cf9SAndrew Gallatin 		 * slice for now
38091e413cf9SAndrew Gallatin 		 */
38101e413cf9SAndrew Gallatin 		if (i > 0)
38111e413cf9SAndrew Gallatin 			continue;
38121e413cf9SAndrew Gallatin 
38131e413cf9SAndrew Gallatin 		bytes = sizeof (*ss->fw_stats);
38141e413cf9SAndrew Gallatin 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
38151e413cf9SAndrew Gallatin 				     sizeof (*ss->fw_stats), 64);
38161e413cf9SAndrew Gallatin 		if (err != 0)
38171e413cf9SAndrew Gallatin 			goto abort;
38181e413cf9SAndrew Gallatin 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
38191e413cf9SAndrew Gallatin 		snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name),
38201e413cf9SAndrew Gallatin 			 "%s:tx(%d)", device_get_nameunit(sc->dev), i);
38211e413cf9SAndrew Gallatin 		mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF);
38221e413cf9SAndrew Gallatin 	}
38231e413cf9SAndrew Gallatin 
38241e413cf9SAndrew Gallatin 	return (0);
38251e413cf9SAndrew Gallatin 
38261e413cf9SAndrew Gallatin abort:
38271e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
38281e413cf9SAndrew Gallatin 	return (ENOMEM);
38291e413cf9SAndrew Gallatin }
38301e413cf9SAndrew Gallatin 
38311e413cf9SAndrew Gallatin static void
38321e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc)
38331e413cf9SAndrew Gallatin {
38341e413cf9SAndrew Gallatin 	mxge_cmd_t cmd;
38351e413cf9SAndrew Gallatin 	char *old_fw;
38361e413cf9SAndrew Gallatin 	int msix_cnt, status, max_intr_slots;
38371e413cf9SAndrew Gallatin 
38381e413cf9SAndrew Gallatin 	sc->num_slices = 1;
38391e413cf9SAndrew Gallatin 	/*
38401e413cf9SAndrew Gallatin 	 *  don't enable multiple slices if they are not enabled,
38411e413cf9SAndrew Gallatin 	 *  or if this is not an SMP system
38421e413cf9SAndrew Gallatin 	 */
38431e413cf9SAndrew Gallatin 
38441e413cf9SAndrew Gallatin 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2)
38451e413cf9SAndrew Gallatin 		return;
38461e413cf9SAndrew Gallatin 
38471e413cf9SAndrew Gallatin 	/* see how many MSI-X interrupts are available */
38481e413cf9SAndrew Gallatin 	msix_cnt = pci_msix_count(sc->dev);
38491e413cf9SAndrew Gallatin 	if (msix_cnt < 2)
38501e413cf9SAndrew Gallatin 		return;
38511e413cf9SAndrew Gallatin 
38521e413cf9SAndrew Gallatin 	/* now load the slice aware firmware see what it supports */
38531e413cf9SAndrew Gallatin 	old_fw = sc->fw_name;
38541e413cf9SAndrew Gallatin 	if (old_fw == mxge_fw_aligned)
38551e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_aligned;
38561e413cf9SAndrew Gallatin 	else
38571e413cf9SAndrew Gallatin 		sc->fw_name = mxge_fw_rss_unaligned;
38581e413cf9SAndrew Gallatin 	status = mxge_load_firmware(sc, 0);
38591e413cf9SAndrew Gallatin 	if (status != 0) {
38601e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Falling back to a single slice\n");
38611e413cf9SAndrew Gallatin 		return;
38621e413cf9SAndrew Gallatin 	}
38631e413cf9SAndrew Gallatin 
38641e413cf9SAndrew Gallatin 	/* try to send a reset command to the card to see if it
38651e413cf9SAndrew Gallatin 	   is alive */
38661e413cf9SAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
38671e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38681e413cf9SAndrew Gallatin 	if (status != 0) {
38691e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
38701e413cf9SAndrew Gallatin 		goto abort_with_fw;
38711e413cf9SAndrew Gallatin 	}
38721e413cf9SAndrew Gallatin 
38731e413cf9SAndrew Gallatin 	/* get rx ring size */
38741e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38751e413cf9SAndrew Gallatin 	if (status != 0) {
38761e413cf9SAndrew Gallatin 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38771e413cf9SAndrew Gallatin 		goto abort_with_fw;
38781e413cf9SAndrew Gallatin 	}
38791e413cf9SAndrew Gallatin 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38801e413cf9SAndrew Gallatin 
38811e413cf9SAndrew Gallatin 	/* tell it the size of the interrupt queues */
38821e413cf9SAndrew Gallatin 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38831e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38841e413cf9SAndrew Gallatin 	if (status != 0) {
38851e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38861e413cf9SAndrew Gallatin 		goto abort_with_fw;
38871e413cf9SAndrew Gallatin 	}
38881e413cf9SAndrew Gallatin 
38891e413cf9SAndrew Gallatin 	/* ask the maximum number of slices it supports */
38901e413cf9SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38911e413cf9SAndrew Gallatin 	if (status != 0) {
38921e413cf9SAndrew Gallatin 		device_printf(sc->dev,
38931e413cf9SAndrew Gallatin 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38941e413cf9SAndrew Gallatin 		goto abort_with_fw;
38951e413cf9SAndrew Gallatin 	}
38961e413cf9SAndrew Gallatin 	sc->num_slices = cmd.data0;
38971e413cf9SAndrew Gallatin 	if (sc->num_slices > msix_cnt)
38981e413cf9SAndrew Gallatin 		sc->num_slices = msix_cnt;
38991e413cf9SAndrew Gallatin 
39001e413cf9SAndrew Gallatin 	if (mxge_max_slices == -1) {
39011e413cf9SAndrew Gallatin 		/* cap to number of CPUs in system */
39021e413cf9SAndrew Gallatin 		if (sc->num_slices > mp_ncpus)
39031e413cf9SAndrew Gallatin 			sc->num_slices = mp_ncpus;
39041e413cf9SAndrew Gallatin 	} else {
39051e413cf9SAndrew Gallatin 		if (sc->num_slices > mxge_max_slices)
39061e413cf9SAndrew Gallatin 			sc->num_slices = mxge_max_slices;
39071e413cf9SAndrew Gallatin 	}
39081e413cf9SAndrew Gallatin 	/* make sure it is a power of two */
39091e413cf9SAndrew Gallatin 	while (sc->num_slices & (sc->num_slices - 1))
39101e413cf9SAndrew Gallatin 		sc->num_slices--;
39111e413cf9SAndrew Gallatin 
39121e413cf9SAndrew Gallatin 	if (mxge_verbose)
39131e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d slices\n",
39141e413cf9SAndrew Gallatin 			      sc->num_slices);
39151e413cf9SAndrew Gallatin 
39161e413cf9SAndrew Gallatin 	return;
39171e413cf9SAndrew Gallatin 
39181e413cf9SAndrew Gallatin abort_with_fw:
39191e413cf9SAndrew Gallatin 	sc->fw_name = old_fw;
39201e413cf9SAndrew Gallatin 	(void) mxge_load_firmware(sc, 0);
39211e413cf9SAndrew Gallatin }
39221e413cf9SAndrew Gallatin 
39231e413cf9SAndrew Gallatin static int
39241e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc)
39251e413cf9SAndrew Gallatin {
39261e413cf9SAndrew Gallatin 	size_t bytes;
39271e413cf9SAndrew Gallatin 	int count, err, i, rid;
39281e413cf9SAndrew Gallatin 
39291e413cf9SAndrew Gallatin 	rid = PCIR_BAR(2);
39301e413cf9SAndrew Gallatin 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
39311e413cf9SAndrew Gallatin 						    &rid, RF_ACTIVE);
39321e413cf9SAndrew Gallatin 
39331e413cf9SAndrew Gallatin 	if (sc->msix_table_res == NULL) {
39341e413cf9SAndrew Gallatin 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
39351e413cf9SAndrew Gallatin 		return ENXIO;
39361e413cf9SAndrew Gallatin 	}
39371e413cf9SAndrew Gallatin 
39381e413cf9SAndrew Gallatin 	count = sc->num_slices;
39391e413cf9SAndrew Gallatin 	err = pci_alloc_msix(sc->dev, &count);
39401e413cf9SAndrew Gallatin 	if (err != 0) {
39411e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39421e413cf9SAndrew Gallatin 			      "err = %d \n", sc->num_slices, err);
39431e413cf9SAndrew Gallatin 		goto abort_with_msix_table;
39441e413cf9SAndrew Gallatin 	}
39451e413cf9SAndrew Gallatin 	if (count < sc->num_slices) {
39461e413cf9SAndrew Gallatin 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39471e413cf9SAndrew Gallatin 			      count, sc->num_slices);
39481e413cf9SAndrew Gallatin 		device_printf(sc->dev,
39491e413cf9SAndrew Gallatin 			      "Try setting hw.mxge.max_slices to %d\n",
39501e413cf9SAndrew Gallatin 			      count);
39511e413cf9SAndrew Gallatin 		err = ENOSPC;
39521e413cf9SAndrew Gallatin 		goto abort_with_msix;
39531e413cf9SAndrew Gallatin 	}
39541e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
39551e413cf9SAndrew Gallatin 	sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39561e413cf9SAndrew Gallatin 	if (sc->msix_irq_res == NULL) {
39571e413cf9SAndrew Gallatin 		err = ENOMEM;
39581e413cf9SAndrew Gallatin 		goto abort_with_msix;
39591e413cf9SAndrew Gallatin 	}
39601e413cf9SAndrew Gallatin 
39611e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39621e413cf9SAndrew Gallatin 		rid = i + 1;
39631e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39641e413cf9SAndrew Gallatin 							  SYS_RES_IRQ,
39651e413cf9SAndrew Gallatin 							  &rid, RF_ACTIVE);
39661e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] == NULL) {
39671e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't allocate IRQ res"
39681e413cf9SAndrew Gallatin 				      " for message %d\n", i);
39691e413cf9SAndrew Gallatin 			err = ENXIO;
39701e413cf9SAndrew Gallatin 			goto abort_with_res;
39711e413cf9SAndrew Gallatin 		}
39721e413cf9SAndrew Gallatin 	}
39731e413cf9SAndrew Gallatin 
39741e413cf9SAndrew Gallatin 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
39751e413cf9SAndrew Gallatin 	sc->msix_ih =  malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39761e413cf9SAndrew Gallatin 
39771e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
39781e413cf9SAndrew Gallatin 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39791e413cf9SAndrew Gallatin 				     INTR_TYPE_NET | INTR_MPSAFE,
398037d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
398137d89b0cSAndrew Gallatin 				     NULL,
398237d89b0cSAndrew Gallatin #endif
398337d89b0cSAndrew Gallatin 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i]);
39841e413cf9SAndrew Gallatin 		if (err != 0) {
39851e413cf9SAndrew Gallatin 			device_printf(sc->dev, "couldn't setup intr for "
39861e413cf9SAndrew Gallatin 				      "message %d\n", i);
39871e413cf9SAndrew Gallatin 			goto abort_with_intr;
39881e413cf9SAndrew Gallatin 		}
39891e413cf9SAndrew Gallatin 	}
39901e413cf9SAndrew Gallatin 
39911e413cf9SAndrew Gallatin 	if (mxge_verbose) {
39921e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %d msix IRQs:",
39931e413cf9SAndrew Gallatin 			      sc->num_slices);
39941e413cf9SAndrew Gallatin 		for (i = 0; i < sc->num_slices; i++)
39951e413cf9SAndrew Gallatin 			printf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39961e413cf9SAndrew Gallatin 		printf("\n");
39971e413cf9SAndrew Gallatin 	}
39981e413cf9SAndrew Gallatin 	return (0);
39991e413cf9SAndrew Gallatin 
40001e413cf9SAndrew Gallatin abort_with_intr:
40011e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40021e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
40031e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40041e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
40051e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
40061e413cf9SAndrew Gallatin 		}
40071e413cf9SAndrew Gallatin 	}
40081e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
40091e413cf9SAndrew Gallatin 
40101e413cf9SAndrew Gallatin 
40111e413cf9SAndrew Gallatin abort_with_res:
40121e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40131e413cf9SAndrew Gallatin 		rid = i + 1;
40141e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
40151e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40161e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
40171e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
40181e413cf9SAndrew Gallatin 	}
40191e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
40201e413cf9SAndrew Gallatin 
40211e413cf9SAndrew Gallatin 
40221e413cf9SAndrew Gallatin abort_with_msix:
40231e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
40241e413cf9SAndrew Gallatin 
40251e413cf9SAndrew Gallatin abort_with_msix_table:
40261e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40271e413cf9SAndrew Gallatin 			     sc->msix_table_res);
40281e413cf9SAndrew Gallatin 
40291e413cf9SAndrew Gallatin 	return err;
40301e413cf9SAndrew Gallatin }
40311e413cf9SAndrew Gallatin 
40321e413cf9SAndrew Gallatin static int
40331e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc)
40341e413cf9SAndrew Gallatin {
40351e413cf9SAndrew Gallatin 	int count, err, rid;
40361e413cf9SAndrew Gallatin 
40371e413cf9SAndrew Gallatin 	count = pci_msi_count(sc->dev);
40381e413cf9SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
40391e413cf9SAndrew Gallatin 		rid = 1;
40401e413cf9SAndrew Gallatin 	} else {
40411e413cf9SAndrew Gallatin 		rid = 0;
404291ed8913SAndrew Gallatin 		sc->legacy_irq = 1;
40431e413cf9SAndrew Gallatin 	}
40441e413cf9SAndrew Gallatin 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
40451e413cf9SAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
40461e413cf9SAndrew Gallatin 	if (sc->irq_res == NULL) {
40471e413cf9SAndrew Gallatin 		device_printf(sc->dev, "could not alloc interrupt\n");
40481e413cf9SAndrew Gallatin 		return ENXIO;
40491e413cf9SAndrew Gallatin 	}
40501e413cf9SAndrew Gallatin 	if (mxge_verbose)
40511e413cf9SAndrew Gallatin 		device_printf(sc->dev, "using %s irq %ld\n",
405291ed8913SAndrew Gallatin 			      sc->legacy_irq ? "INTx" : "MSI",
40531e413cf9SAndrew Gallatin 			      rman_get_start(sc->irq_res));
40541e413cf9SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
40551e413cf9SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
405637d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030
405737d89b0cSAndrew Gallatin 			     NULL,
405837d89b0cSAndrew Gallatin #endif
405937d89b0cSAndrew Gallatin 			     mxge_intr, &sc->ss[0], &sc->ih);
40601e413cf9SAndrew Gallatin 	if (err != 0) {
40611e413cf9SAndrew Gallatin 		bus_release_resource(sc->dev, SYS_RES_IRQ,
406291ed8913SAndrew Gallatin 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
406391ed8913SAndrew Gallatin 		if (!sc->legacy_irq)
40641e413cf9SAndrew Gallatin 			pci_release_msi(sc->dev);
40651e413cf9SAndrew Gallatin 	}
40661e413cf9SAndrew Gallatin 	return err;
40671e413cf9SAndrew Gallatin }
40681e413cf9SAndrew Gallatin 
40691e413cf9SAndrew Gallatin static void
40701e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc)
40711e413cf9SAndrew Gallatin {
40721e413cf9SAndrew Gallatin 	int i, rid;
40731e413cf9SAndrew Gallatin 
40741e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40751e413cf9SAndrew Gallatin 		if (sc->msix_ih[i] != NULL) {
40761e413cf9SAndrew Gallatin 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40771e413cf9SAndrew Gallatin 					  sc->msix_ih[i]);
40781e413cf9SAndrew Gallatin 			sc->msix_ih[i] = NULL;
40791e413cf9SAndrew Gallatin 		}
40801e413cf9SAndrew Gallatin 	}
40811e413cf9SAndrew Gallatin 	free(sc->msix_ih, M_DEVBUF);
40821e413cf9SAndrew Gallatin 
40831e413cf9SAndrew Gallatin 	for (i = 0; i < sc->num_slices; i++) {
40841e413cf9SAndrew Gallatin 		rid = i + 1;
40851e413cf9SAndrew Gallatin 		if (sc->msix_irq_res[i] != NULL)
40861e413cf9SAndrew Gallatin 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40871e413cf9SAndrew Gallatin 					     sc->msix_irq_res[i]);
40881e413cf9SAndrew Gallatin 		sc->msix_irq_res[i] = NULL;
40891e413cf9SAndrew Gallatin 	}
40901e413cf9SAndrew Gallatin 	free(sc->msix_irq_res, M_DEVBUF);
40911e413cf9SAndrew Gallatin 
40921e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40931e413cf9SAndrew Gallatin 			     sc->msix_table_res);
40941e413cf9SAndrew Gallatin 
40951e413cf9SAndrew Gallatin 	pci_release_msi(sc->dev);
40961e413cf9SAndrew Gallatin 	return;
40971e413cf9SAndrew Gallatin }
40981e413cf9SAndrew Gallatin 
40991e413cf9SAndrew Gallatin static void
41001e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc)
41011e413cf9SAndrew Gallatin {
41021e413cf9SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
41031e413cf9SAndrew Gallatin 	bus_release_resource(sc->dev, SYS_RES_IRQ,
410491ed8913SAndrew Gallatin 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
410591ed8913SAndrew Gallatin 	if (!sc->legacy_irq)
41061e413cf9SAndrew Gallatin 		pci_release_msi(sc->dev);
41071e413cf9SAndrew Gallatin }
41081e413cf9SAndrew Gallatin 
41091e413cf9SAndrew Gallatin static void
41101e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc)
41111e413cf9SAndrew Gallatin {
41121e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
41131e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
41141e413cf9SAndrew Gallatin 	else
41151e413cf9SAndrew Gallatin 		mxge_rem_single_irq(sc);
41161e413cf9SAndrew Gallatin }
41171e413cf9SAndrew Gallatin 
41181e413cf9SAndrew Gallatin static int
41191e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc)
41201e413cf9SAndrew Gallatin {
41211e413cf9SAndrew Gallatin 	int err;
41221e413cf9SAndrew Gallatin 
41231e413cf9SAndrew Gallatin 	if (sc->num_slices > 1)
41241e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
41251e413cf9SAndrew Gallatin 	else
41261e413cf9SAndrew Gallatin 		err = mxge_add_single_irq(sc);
41271e413cf9SAndrew Gallatin 
41281e413cf9SAndrew Gallatin 	if (0 && err == 0 && sc->num_slices > 1) {
41291e413cf9SAndrew Gallatin 		mxge_rem_msix_irqs(sc);
41301e413cf9SAndrew Gallatin 		err = mxge_add_msix_irqs(sc);
41311e413cf9SAndrew Gallatin 	}
41321e413cf9SAndrew Gallatin 	return err;
41331e413cf9SAndrew Gallatin }
41341e413cf9SAndrew Gallatin 
4135b2fc195eSAndrew Gallatin 
4136b2fc195eSAndrew Gallatin static int
41376d87a65dSAndrew Gallatin mxge_attach(device_t dev)
4138b2fc195eSAndrew Gallatin {
41396d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4140b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
41411e413cf9SAndrew Gallatin 	int err, rid;
4142b2fc195eSAndrew Gallatin 
4143b2fc195eSAndrew Gallatin 	sc->dev = dev;
41446d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
4145b2fc195eSAndrew Gallatin 
4146b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
4147b2fc195eSAndrew Gallatin 				 1,			/* alignment */
41481e413cf9SAndrew Gallatin 				 0,			/* boundary */
4149b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
4150b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
4151b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
4152aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
41535e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
41541e413cf9SAndrew Gallatin 				 65536,			/* maxsegsize */
4155b2fc195eSAndrew Gallatin 				 0,			/* flags */
4156b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
4157b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
4158b2fc195eSAndrew Gallatin 
4159b2fc195eSAndrew Gallatin 	if (err != 0) {
4160b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
4161b2fc195eSAndrew Gallatin 			      err);
4162b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
4163b2fc195eSAndrew Gallatin 	}
4164b2fc195eSAndrew Gallatin 
4165b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
4166b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
4167b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
4168b2fc195eSAndrew Gallatin 		err = ENOSPC;
4169b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
4170b2fc195eSAndrew Gallatin 	}
41711e413cf9SAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
41721e413cf9SAndrew Gallatin 
4173a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
4174a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
4175a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
4176a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
4177a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
4178a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
4179b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
4180b2fc195eSAndrew Gallatin 
4181dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
4182d91b1b49SAndrew Gallatin 
4183dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
4184b2fc195eSAndrew Gallatin 
4185b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
4186b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
4187b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
4188b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
4189b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
4190b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
4191b2fc195eSAndrew Gallatin 		err = ENXIO;
4192b2fc195eSAndrew Gallatin 		goto abort_with_lock;
4193b2fc195eSAndrew Gallatin 	}
4194b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
4195b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
4196b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
4197b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
4198b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
4199b2fc195eSAndrew Gallatin 		err = ENXIO;
4200b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4201b2fc195eSAndrew Gallatin 	}
4202b2fc195eSAndrew Gallatin 
4203b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
4204b2fc195eSAndrew Gallatin 	   lanai SRAM */
42056d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
4206b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
4207b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
42086d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
4209b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
42106d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
42116d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
4212b2fc195eSAndrew Gallatin 	if (err != 0)
4213b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4214b2fc195eSAndrew Gallatin 
4215b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
42166d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
4217b2fc195eSAndrew Gallatin 
4218b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
42196d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
42206d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
4221b2fc195eSAndrew Gallatin 	if (err != 0)
4222b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
4223b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
42246d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4225b2fc195eSAndrew Gallatin 	if (err != 0)
4226b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
4227b2fc195eSAndrew Gallatin 
4228a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4229a98d6cd7SAndrew Gallatin 	if (err != 0)
42301e413cf9SAndrew Gallatin 		goto abort_with_zeropad_dma;
4231b2fc195eSAndrew Gallatin 
42328fe615baSAndrew Gallatin 	/* select & load the firmware */
42338fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
4234b2fc195eSAndrew Gallatin 	if (err != 0)
42351e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42365e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
42371e413cf9SAndrew Gallatin 
42381e413cf9SAndrew Gallatin 	mxge_slice_probe(sc);
42391e413cf9SAndrew Gallatin 	err = mxge_alloc_slices(sc);
42401e413cf9SAndrew Gallatin 	if (err != 0)
42411e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
42421e413cf9SAndrew Gallatin 
4243adae7080SAndrew Gallatin 	err = mxge_reset(sc, 0);
4244b2fc195eSAndrew Gallatin 	if (err != 0)
42451e413cf9SAndrew Gallatin 		goto abort_with_slices;
4246b2fc195eSAndrew Gallatin 
4247a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
4248a98d6cd7SAndrew Gallatin 	if (err != 0) {
4249a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
42501e413cf9SAndrew Gallatin 		goto abort_with_dmabench;
4251a98d6cd7SAndrew Gallatin 	}
4252a98d6cd7SAndrew Gallatin 
42531e413cf9SAndrew Gallatin 	err = mxge_add_irq(sc);
4254a98d6cd7SAndrew Gallatin 	if (err != 0) {
42551e413cf9SAndrew Gallatin 		device_printf(sc->dev, "failed to add irq\n");
4256a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
4257a98d6cd7SAndrew Gallatin 	}
42581e413cf9SAndrew Gallatin 
4259b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
4260c792928fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
426137d89b0cSAndrew Gallatin 		IFCAP_VLAN_MTU | IFCAP_LRO;
426237d89b0cSAndrew Gallatin 
426337d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API
426437d89b0cSAndrew Gallatin 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
426537d89b0cSAndrew Gallatin #endif
4266c792928fSAndrew Gallatin 
4267053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
4268053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
4269053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
4270053e637fSAndrew Gallatin 	else
4271053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
4272adae7080SAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support\n",
4273053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
4274aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
4275b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
4276f04b33f8SAndrew Gallatin 	if (sc->lro_cnt == 0)
4277f04b33f8SAndrew Gallatin 		ifp->if_capenable &= ~IFCAP_LRO;
42785e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
42796d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
4280b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
4281b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
42826d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
42836d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
4284c587e59fSAndrew Gallatin 	/* Initialise the ifmedia structure */
4285c587e59fSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
4286c587e59fSAndrew Gallatin 		     mxge_media_status);
4287c587e59fSAndrew Gallatin 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
4288c587e59fSAndrew Gallatin 	mxge_media_probe(sc);
4289b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
4290b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
4291053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
4292c792928fSAndrew Gallatin 		ifp->if_mtu = 9000;
4293b2fc195eSAndrew Gallatin 
42946d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
4295b2fc195eSAndrew Gallatin 	return 0;
4296b2fc195eSAndrew Gallatin 
4297a98d6cd7SAndrew Gallatin abort_with_rings:
4298a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
42991e413cf9SAndrew Gallatin abort_with_slices:
43001e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4301a98d6cd7SAndrew Gallatin abort_with_dmabench:
4302a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
4303b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
43046d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
4305b2fc195eSAndrew Gallatin abort_with_cmd_dma:
43066d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4307b2fc195eSAndrew Gallatin abort_with_mem_res:
4308b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4309b2fc195eSAndrew Gallatin abort_with_lock:
4310b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4311a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4312a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4313b2fc195eSAndrew Gallatin 	if_free(ifp);
4314b2fc195eSAndrew Gallatin abort_with_parent_dmat:
4315b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4316b2fc195eSAndrew Gallatin 
4317b2fc195eSAndrew Gallatin abort_with_nothing:
4318b2fc195eSAndrew Gallatin 	return err;
4319b2fc195eSAndrew Gallatin }
4320b2fc195eSAndrew Gallatin 
4321b2fc195eSAndrew Gallatin static int
43226d87a65dSAndrew Gallatin mxge_detach(device_t dev)
4323b2fc195eSAndrew Gallatin {
43246d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
4325b2fc195eSAndrew Gallatin 
432637d89b0cSAndrew Gallatin 	if (mxge_vlans_active(sc)) {
4327c792928fSAndrew Gallatin 		device_printf(sc->dev,
4328c792928fSAndrew Gallatin 			      "Detach vlans before removing module\n");
4329c792928fSAndrew Gallatin 		return EBUSY;
4330c792928fSAndrew Gallatin 	}
4331a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
43321e413cf9SAndrew Gallatin 	callout_stop(&sc->co_hdl);
4333b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
43346d87a65dSAndrew Gallatin 		mxge_close(sc);
4335a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
4336b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
4337dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
4338091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
43391e413cf9SAndrew Gallatin 	mxge_rem_sysctls(sc);
43401e413cf9SAndrew Gallatin 	mxge_rem_irq(sc);
4341a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
43421e413cf9SAndrew Gallatin 	mxge_free_slices(sc);
4343a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
43446d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
43456d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
4346b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
4347b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
4348a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
4349a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
4350b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
4351b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
4352b2fc195eSAndrew Gallatin 	return 0;
4353b2fc195eSAndrew Gallatin }
4354b2fc195eSAndrew Gallatin 
4355b2fc195eSAndrew Gallatin static int
43566d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
4357b2fc195eSAndrew Gallatin {
4358b2fc195eSAndrew Gallatin 	return 0;
4359b2fc195eSAndrew Gallatin }
4360b2fc195eSAndrew Gallatin 
4361b2fc195eSAndrew Gallatin /*
4362b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
4363b2fc195eSAndrew Gallatin 
4364b2fc195eSAndrew Gallatin   Local Variables:
4365b2fc195eSAndrew Gallatin   c-file-style:"linux"
4366b2fc195eSAndrew Gallatin   tab-width:8
4367b2fc195eSAndrew Gallatin   End:
4368b2fc195eSAndrew Gallatin */
4369