xref: /freebsd/sys/dev/mxge/if_mxge.c (revision 053e637f86d273f80e5371a70b8f4be46cc98592)
16d87a65dSAndrew Gallatin /******************************************************************************
2b2fc195eSAndrew Gallatin 
3b2fc195eSAndrew Gallatin Copyright (c) 2006, 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 
12b2fc195eSAndrew Gallatin  2. Redistributions in binary form must reproduce the above copyright
13b2fc195eSAndrew Gallatin     notice, this list of conditions and the following disclaimer in the
14b2fc195eSAndrew Gallatin     documentation and/or other materials provided with the distribution.
15b2fc195eSAndrew Gallatin 
16b2fc195eSAndrew Gallatin  3. Neither the name of the Myricom Inc, nor the names of its
17b2fc195eSAndrew Gallatin     contributors may be used to endorse or promote products derived from
18b2fc195eSAndrew Gallatin     this software without specific prior written permission.
19b2fc195eSAndrew Gallatin 
20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE.
31b2fc195eSAndrew Gallatin 
32b2fc195eSAndrew Gallatin ***************************************************************************/
33b2fc195eSAndrew Gallatin 
34b2fc195eSAndrew Gallatin #include <sys/cdefs.h>
35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$");
36b2fc195eSAndrew Gallatin 
37b2fc195eSAndrew Gallatin #include <sys/param.h>
38b2fc195eSAndrew Gallatin #include <sys/systm.h>
39b2fc195eSAndrew Gallatin #include <sys/linker.h>
40b2fc195eSAndrew Gallatin #include <sys/firmware.h>
41b2fc195eSAndrew Gallatin #include <sys/endian.h>
42b2fc195eSAndrew Gallatin #include <sys/sockio.h>
43b2fc195eSAndrew Gallatin #include <sys/mbuf.h>
44b2fc195eSAndrew Gallatin #include <sys/malloc.h>
45b2fc195eSAndrew Gallatin #include <sys/kdb.h>
46b2fc195eSAndrew Gallatin #include <sys/kernel.h>
474e7f640dSJohn Baldwin #include <sys/lock.h>
48b2fc195eSAndrew Gallatin #include <sys/module.h>
49b2fc195eSAndrew Gallatin #include <sys/memrange.h>
50b2fc195eSAndrew Gallatin #include <sys/socket.h>
51b2fc195eSAndrew Gallatin #include <sys/sysctl.h>
52b2fc195eSAndrew Gallatin #include <sys/sx.h>
53b2fc195eSAndrew Gallatin 
54b2fc195eSAndrew Gallatin #include <net/if.h>
55b2fc195eSAndrew Gallatin #include <net/if_arp.h>
56b2fc195eSAndrew Gallatin #include <net/ethernet.h>
57b2fc195eSAndrew Gallatin #include <net/if_dl.h>
58b2fc195eSAndrew Gallatin #include <net/if_media.h>
59b2fc195eSAndrew Gallatin 
60b2fc195eSAndrew Gallatin #include <net/bpf.h>
61b2fc195eSAndrew Gallatin 
62b2fc195eSAndrew Gallatin #include <net/if_types.h>
63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h>
64b2fc195eSAndrew Gallatin #include <net/zlib.h>
65b2fc195eSAndrew Gallatin 
66b2fc195eSAndrew Gallatin #include <netinet/in_systm.h>
67b2fc195eSAndrew Gallatin #include <netinet/in.h>
68b2fc195eSAndrew Gallatin #include <netinet/ip.h>
69aed8e389SAndrew Gallatin #include <netinet/tcp.h>
70b2fc195eSAndrew Gallatin 
71b2fc195eSAndrew Gallatin #include <machine/bus.h>
72053e637fSAndrew Gallatin #include <machine/in_cksum.h>
73b2fc195eSAndrew Gallatin #include <machine/resource.h>
74b2fc195eSAndrew Gallatin #include <sys/bus.h>
75b2fc195eSAndrew Gallatin #include <sys/rman.h>
76b2fc195eSAndrew Gallatin 
77b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h>
78b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h>
79b2fc195eSAndrew Gallatin 
80b2fc195eSAndrew Gallatin #include <vm/vm.h>		/* for pmap_mapdev() */
81b2fc195eSAndrew Gallatin #include <vm/pmap.h>
82b2fc195eSAndrew Gallatin 
836d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h>
846d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h>
856d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h>
86b2fc195eSAndrew Gallatin 
87b2fc195eSAndrew Gallatin /* tunable params */
886d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1;
89d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0;
905e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024;
916d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30;
925e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1;
936d87a65dSAndrew Gallatin static int mxge_flow_control = 1;
945e7d8541SAndrew Gallatin static int mxge_verbose = 0;
95dce01b9bSAndrew Gallatin static int mxge_ticks;
966d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e";
976d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e";
98b2fc195eSAndrew Gallatin 
996d87a65dSAndrew Gallatin static int mxge_probe(device_t dev);
1006d87a65dSAndrew Gallatin static int mxge_attach(device_t dev);
1016d87a65dSAndrew Gallatin static int mxge_detach(device_t dev);
1026d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev);
1036d87a65dSAndrew Gallatin static void mxge_intr(void *arg);
104b2fc195eSAndrew Gallatin 
1056d87a65dSAndrew Gallatin static device_method_t mxge_methods[] =
106b2fc195eSAndrew Gallatin {
107b2fc195eSAndrew Gallatin   /* Device interface */
1086d87a65dSAndrew Gallatin   DEVMETHOD(device_probe, mxge_probe),
1096d87a65dSAndrew Gallatin   DEVMETHOD(device_attach, mxge_attach),
1106d87a65dSAndrew Gallatin   DEVMETHOD(device_detach, mxge_detach),
1116d87a65dSAndrew Gallatin   DEVMETHOD(device_shutdown, mxge_shutdown),
112b2fc195eSAndrew Gallatin   {0, 0}
113b2fc195eSAndrew Gallatin };
114b2fc195eSAndrew Gallatin 
1156d87a65dSAndrew Gallatin static driver_t mxge_driver =
116b2fc195eSAndrew Gallatin {
1176d87a65dSAndrew Gallatin   "mxge",
1186d87a65dSAndrew Gallatin   mxge_methods,
1196d87a65dSAndrew Gallatin   sizeof(mxge_softc_t),
120b2fc195eSAndrew Gallatin };
121b2fc195eSAndrew Gallatin 
1226d87a65dSAndrew Gallatin static devclass_t mxge_devclass;
123b2fc195eSAndrew Gallatin 
124b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/
1256d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1266d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1);
127b2fc195eSAndrew Gallatin 
1288fe615baSAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc);
1298fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1308fe615baSAndrew Gallatin 
131b2fc195eSAndrew Gallatin static int
1326d87a65dSAndrew Gallatin mxge_probe(device_t dev)
133b2fc195eSAndrew Gallatin {
1346d87a65dSAndrew Gallatin   if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1356d87a65dSAndrew Gallatin       (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) {
136b2fc195eSAndrew Gallatin 	  device_set_desc(dev, "Myri10G-PCIE-8A");
137b2fc195eSAndrew Gallatin 	  return 0;
138b2fc195eSAndrew Gallatin   }
139b2fc195eSAndrew Gallatin   return ENXIO;
140b2fc195eSAndrew Gallatin }
141b2fc195eSAndrew Gallatin 
142b2fc195eSAndrew Gallatin static void
1436d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc)
144b2fc195eSAndrew Gallatin {
145b2fc195eSAndrew Gallatin 	struct mem_range_desc mrdesc;
146b2fc195eSAndrew Gallatin 	vm_paddr_t pa;
147b2fc195eSAndrew Gallatin 	vm_offset_t len;
148b2fc195eSAndrew Gallatin 	int err, action;
149b2fc195eSAndrew Gallatin 
150b2fc195eSAndrew Gallatin 	pa = rman_get_start(sc->mem_res);
151b2fc195eSAndrew Gallatin 	len = rman_get_size(sc->mem_res);
152b2fc195eSAndrew Gallatin 	mrdesc.mr_base = pa;
153b2fc195eSAndrew Gallatin 	mrdesc.mr_len = len;
154b2fc195eSAndrew Gallatin 	mrdesc.mr_flags = MDF_WRITECOMBINE;
155b2fc195eSAndrew Gallatin 	action = MEMRANGE_SET_UPDATE;
1566d87a65dSAndrew Gallatin 	strcpy((char *)&mrdesc.mr_owner, "mxge");
157b2fc195eSAndrew Gallatin 	err = mem_range_attr_set(&mrdesc, &action);
158b2fc195eSAndrew Gallatin 	if (err != 0) {
159b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
160b2fc195eSAndrew Gallatin 			      "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n",
161b2fc195eSAndrew Gallatin 			      (unsigned long)pa, (unsigned long)len, err);
162b2fc195eSAndrew Gallatin 	} else {
163b2fc195eSAndrew Gallatin 		sc->wc = 1;
164b2fc195eSAndrew Gallatin 	}
165b2fc195eSAndrew Gallatin }
166b2fc195eSAndrew Gallatin 
167b2fc195eSAndrew Gallatin 
168b2fc195eSAndrew Gallatin /* callback to get our DMA address */
169b2fc195eSAndrew Gallatin static void
1706d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
171b2fc195eSAndrew Gallatin 			 int error)
172b2fc195eSAndrew Gallatin {
173b2fc195eSAndrew Gallatin 	if (error == 0) {
174b2fc195eSAndrew Gallatin 		*(bus_addr_t *) arg = segs->ds_addr;
175b2fc195eSAndrew Gallatin 	}
176b2fc195eSAndrew Gallatin }
177b2fc195eSAndrew Gallatin 
178b2fc195eSAndrew Gallatin static int
1796d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
180b2fc195eSAndrew Gallatin 		   bus_size_t alignment)
181b2fc195eSAndrew Gallatin {
182b2fc195eSAndrew Gallatin 	int err;
183b2fc195eSAndrew Gallatin 	device_t dev = sc->dev;
184b2fc195eSAndrew Gallatin 
185b2fc195eSAndrew Gallatin 	/* allocate DMAable memory tags */
186b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
187b2fc195eSAndrew Gallatin 				 alignment,		/* alignment */
188b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
189b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
190b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
191b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
192b2fc195eSAndrew Gallatin 				 bytes,			/* maxsize */
193b2fc195eSAndrew Gallatin 				 1,			/* num segs */
194b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
195b2fc195eSAndrew Gallatin 				 BUS_DMA_COHERENT,	/* flags */
196b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
197b2fc195eSAndrew Gallatin 				 &dma->dmat);		/* tag */
198b2fc195eSAndrew Gallatin 	if (err != 0) {
199b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
200b2fc195eSAndrew Gallatin 		return err;
201b2fc195eSAndrew Gallatin 	}
202b2fc195eSAndrew Gallatin 
203b2fc195eSAndrew Gallatin 	/* allocate DMAable memory & map */
204b2fc195eSAndrew Gallatin 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
205b2fc195eSAndrew Gallatin 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
206b2fc195eSAndrew Gallatin 				| BUS_DMA_ZERO),  &dma->map);
207b2fc195eSAndrew Gallatin 	if (err != 0) {
208b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
209b2fc195eSAndrew Gallatin 		goto abort_with_dmat;
210b2fc195eSAndrew Gallatin 	}
211b2fc195eSAndrew Gallatin 
212b2fc195eSAndrew Gallatin 	/* load the memory */
213b2fc195eSAndrew Gallatin 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2146d87a65dSAndrew Gallatin 			      mxge_dmamap_callback,
215b2fc195eSAndrew Gallatin 			      (void *)&dma->bus_addr, 0);
216b2fc195eSAndrew Gallatin 	if (err != 0) {
217b2fc195eSAndrew Gallatin 		device_printf(dev, "couldn't load map (err = %d)\n", err);
218b2fc195eSAndrew Gallatin 		goto abort_with_mem;
219b2fc195eSAndrew Gallatin 	}
220b2fc195eSAndrew Gallatin 	return 0;
221b2fc195eSAndrew Gallatin 
222b2fc195eSAndrew Gallatin abort_with_mem:
223b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
224b2fc195eSAndrew Gallatin abort_with_dmat:
225b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
226b2fc195eSAndrew Gallatin 	return err;
227b2fc195eSAndrew Gallatin }
228b2fc195eSAndrew Gallatin 
229b2fc195eSAndrew Gallatin 
230b2fc195eSAndrew Gallatin static void
2316d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma)
232b2fc195eSAndrew Gallatin {
233b2fc195eSAndrew Gallatin 	bus_dmamap_unload(dma->dmat, dma->map);
234b2fc195eSAndrew Gallatin 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
235b2fc195eSAndrew Gallatin 	(void)bus_dma_tag_destroy(dma->dmat);
236b2fc195eSAndrew Gallatin }
237b2fc195eSAndrew Gallatin 
238b2fc195eSAndrew Gallatin /*
239b2fc195eSAndrew Gallatin  * The eeprom strings on the lanaiX have the format
240b2fc195eSAndrew Gallatin  * SN=x\0
241b2fc195eSAndrew Gallatin  * MAC=x:x:x:x:x:x\0
242b2fc195eSAndrew Gallatin  * PC=text\0
243b2fc195eSAndrew Gallatin  */
244b2fc195eSAndrew Gallatin 
245b2fc195eSAndrew Gallatin static int
2466d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc)
247b2fc195eSAndrew Gallatin {
2486d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
249b2fc195eSAndrew Gallatin 
250b2fc195eSAndrew Gallatin 	char *ptr, *limit;
251b2fc195eSAndrew Gallatin 	int i, found_mac;
252b2fc195eSAndrew Gallatin 
253b2fc195eSAndrew Gallatin 	ptr = sc->eeprom_strings;
2546d87a65dSAndrew Gallatin 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
255b2fc195eSAndrew Gallatin 	found_mac = 0;
256b2fc195eSAndrew Gallatin 	while (ptr < limit && *ptr != '\0') {
257b2fc195eSAndrew Gallatin 		if (memcmp(ptr, "MAC=", 4) == 0) {
2585e7d8541SAndrew Gallatin 			ptr += 1;
259b2fc195eSAndrew Gallatin 			sc->mac_addr_string = ptr;
260b2fc195eSAndrew Gallatin 			for (i = 0; i < 6; i++) {
2615e7d8541SAndrew Gallatin 				ptr += 3;
262b2fc195eSAndrew Gallatin 				if ((ptr + 2) > limit)
263b2fc195eSAndrew Gallatin 					goto abort;
264b2fc195eSAndrew Gallatin 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
265b2fc195eSAndrew Gallatin 				found_mac = 1;
266b2fc195eSAndrew Gallatin 			}
2675e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "PC=", 3) == 0) {
2685e7d8541SAndrew Gallatin 			ptr += 3;
2695e7d8541SAndrew Gallatin 			strncpy(sc->product_code_string, ptr,
2705e7d8541SAndrew Gallatin 				sizeof (sc->product_code_string) - 1);
2715e7d8541SAndrew Gallatin 		} else if (memcmp(ptr, "SN=", 3) == 0) {
2725e7d8541SAndrew Gallatin 			ptr += 3;
2735e7d8541SAndrew Gallatin 			strncpy(sc->serial_number_string, ptr,
2745e7d8541SAndrew Gallatin 				sizeof (sc->serial_number_string) - 1);
275b2fc195eSAndrew Gallatin 		}
2766d87a65dSAndrew Gallatin 		MXGE_NEXT_STRING(ptr);
277b2fc195eSAndrew Gallatin 	}
278b2fc195eSAndrew Gallatin 
279b2fc195eSAndrew Gallatin 	if (found_mac)
280b2fc195eSAndrew Gallatin 		return 0;
281b2fc195eSAndrew Gallatin 
282b2fc195eSAndrew Gallatin  abort:
283b2fc195eSAndrew Gallatin 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
284b2fc195eSAndrew Gallatin 
285b2fc195eSAndrew Gallatin 	return ENXIO;
286b2fc195eSAndrew Gallatin }
287b2fc195eSAndrew Gallatin 
288b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__
2898fe615baSAndrew Gallatin static void
2908fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
291b2fc195eSAndrew Gallatin {
292b2fc195eSAndrew Gallatin 	uint32_t val;
2938fe615baSAndrew Gallatin 	unsigned long base, off;
294b2fc195eSAndrew Gallatin 	char *va, *cfgptr;
2958fe615baSAndrew Gallatin 	device_t pdev, mcp55;
2968fe615baSAndrew Gallatin 	uint16_t vendor_id, device_id, word;
297b2fc195eSAndrew Gallatin 	uintptr_t bus, slot, func, ivend, idev;
298b2fc195eSAndrew Gallatin 	uint32_t *ptr32;
299b2fc195eSAndrew Gallatin 
3008fe615baSAndrew Gallatin 
3018fe615baSAndrew Gallatin 	if (!mxge_nvidia_ecrc_enable)
3028fe615baSAndrew Gallatin 		return;
3038fe615baSAndrew Gallatin 
3048fe615baSAndrew Gallatin 	pdev = device_get_parent(device_get_parent(sc->dev));
3058fe615baSAndrew Gallatin 	if (pdev == NULL) {
3068fe615baSAndrew Gallatin 		device_printf(sc->dev, "could not find parent?\n");
3078fe615baSAndrew Gallatin 		return;
3088fe615baSAndrew Gallatin 	}
3098fe615baSAndrew Gallatin 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3108fe615baSAndrew Gallatin 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3118fe615baSAndrew Gallatin 
3128fe615baSAndrew Gallatin 	if (vendor_id != 0x10de)
3138fe615baSAndrew Gallatin 		return;
3148fe615baSAndrew Gallatin 
3158fe615baSAndrew Gallatin 	base = 0;
3168fe615baSAndrew Gallatin 
3178fe615baSAndrew Gallatin 	if (device_id == 0x005d) {
3188fe615baSAndrew Gallatin 		/* ck804, base address is magic */
3198fe615baSAndrew Gallatin 		base = 0xe0000000UL;
3208fe615baSAndrew Gallatin 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3218fe615baSAndrew Gallatin 		/* mcp55, base address stored in chipset */
3228fe615baSAndrew Gallatin 		mcp55 = pci_find_bsf(0, 0, 0);
3238fe615baSAndrew Gallatin 		if (mcp55 &&
3248fe615baSAndrew Gallatin 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3258fe615baSAndrew Gallatin 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3268fe615baSAndrew Gallatin 			word = pci_read_config(mcp55, 0x90, 2);
3278fe615baSAndrew Gallatin 			base = ((unsigned long)word & 0x7ffeU) << 25;
3288fe615baSAndrew Gallatin 		}
3298fe615baSAndrew Gallatin 	}
3308fe615baSAndrew Gallatin 	if (!base)
3318fe615baSAndrew Gallatin 		return;
3328fe615baSAndrew Gallatin 
333b2fc195eSAndrew Gallatin 	/* XXXX
334b2fc195eSAndrew Gallatin 	   Test below is commented because it is believed that doing
335b2fc195eSAndrew Gallatin 	   config read/write beyond 0xff will access the config space
336b2fc195eSAndrew Gallatin 	   for the next larger function.  Uncomment this and remove
337b2fc195eSAndrew Gallatin 	   the hacky pmap_mapdev() way of accessing config space when
338b2fc195eSAndrew Gallatin 	   FreeBSD grows support for extended pcie config space access
339b2fc195eSAndrew Gallatin 	*/
340b2fc195eSAndrew Gallatin #if 0
341b2fc195eSAndrew Gallatin 	/* See if we can, by some miracle, access the extended
342b2fc195eSAndrew Gallatin 	   config space */
343b2fc195eSAndrew Gallatin 	val = pci_read_config(pdev, 0x178, 4);
344b2fc195eSAndrew Gallatin 	if (val != 0xffffffff) {
345b2fc195eSAndrew Gallatin 		val |= 0x40;
346b2fc195eSAndrew Gallatin 		pci_write_config(pdev, 0x178, val, 4);
3478fe615baSAndrew Gallatin 		return;
348b2fc195eSAndrew Gallatin 	}
349b2fc195eSAndrew Gallatin #endif
350b2fc195eSAndrew Gallatin 	/* Rather than using normal pci config space writes, we must
351b2fc195eSAndrew Gallatin 	 * map the Nvidia config space ourselves.  This is because on
352b2fc195eSAndrew Gallatin 	 * opteron/nvidia class machine the 0xe000000 mapping is
353b2fc195eSAndrew Gallatin 	 * handled by the nvidia chipset, that means the internal PCI
354b2fc195eSAndrew Gallatin 	 * device (the on-chip northbridge), or the amd-8131 bridge
355b2fc195eSAndrew Gallatin 	 * and things behind them are not visible by this method.
356b2fc195eSAndrew Gallatin 	 */
357b2fc195eSAndrew Gallatin 
358b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
359b2fc195eSAndrew Gallatin 		      PCI_IVAR_BUS, &bus);
360b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
361b2fc195eSAndrew Gallatin 		      PCI_IVAR_SLOT, &slot);
362b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
363b2fc195eSAndrew Gallatin 		      PCI_IVAR_FUNCTION, &func);
364b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
365b2fc195eSAndrew Gallatin 		      PCI_IVAR_VENDOR, &ivend);
366b2fc195eSAndrew Gallatin 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
367b2fc195eSAndrew Gallatin 		      PCI_IVAR_DEVICE, &idev);
368b2fc195eSAndrew Gallatin 
3698fe615baSAndrew Gallatin 	off =  base
370b2fc195eSAndrew Gallatin 		+ 0x00100000UL * (unsigned long)bus
371b2fc195eSAndrew Gallatin 		+ 0x00001000UL * (unsigned long)(func
372b2fc195eSAndrew Gallatin 						 + 8 * slot);
373b2fc195eSAndrew Gallatin 
374b2fc195eSAndrew Gallatin 	/* map it into the kernel */
375b2fc195eSAndrew Gallatin 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
376b2fc195eSAndrew Gallatin 
377b2fc195eSAndrew Gallatin 
378b2fc195eSAndrew Gallatin 	if (va == NULL) {
379b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3808fe615baSAndrew Gallatin 		return;
381b2fc195eSAndrew Gallatin 	}
382b2fc195eSAndrew Gallatin 	/* get a pointer to the config space mapped into the kernel */
383b2fc195eSAndrew Gallatin 	cfgptr = va + (off & PAGE_MASK);
384b2fc195eSAndrew Gallatin 
385b2fc195eSAndrew Gallatin 	/* make sure that we can really access it */
386b2fc195eSAndrew Gallatin 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
387b2fc195eSAndrew Gallatin 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
388b2fc195eSAndrew Gallatin 	if (! (vendor_id == ivend && device_id == idev)) {
389b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
390b2fc195eSAndrew Gallatin 			      vendor_id, device_id);
391b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3928fe615baSAndrew Gallatin 		return;
393b2fc195eSAndrew Gallatin 	}
394b2fc195eSAndrew Gallatin 
395b2fc195eSAndrew Gallatin 	ptr32 = (uint32_t*)(cfgptr + 0x178);
396b2fc195eSAndrew Gallatin 	val = *ptr32;
397b2fc195eSAndrew Gallatin 
398b2fc195eSAndrew Gallatin 	if (val == 0xffffffff) {
399b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "extended mapping failed\n");
400b2fc195eSAndrew Gallatin 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4018fe615baSAndrew Gallatin 		return;
402b2fc195eSAndrew Gallatin 	}
403b2fc195eSAndrew Gallatin 	*ptr32 = val | 0x40;
404b2fc195eSAndrew Gallatin 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4055e7d8541SAndrew Gallatin 	if (mxge_verbose)
406b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
4075e7d8541SAndrew Gallatin 			      "Enabled ECRC on upstream Nvidia bridge "
4085e7d8541SAndrew Gallatin 			      "at %d:%d:%d\n",
409b2fc195eSAndrew Gallatin 			      (int)bus, (int)slot, (int)func);
4108fe615baSAndrew Gallatin 	return;
411b2fc195eSAndrew Gallatin }
412b2fc195eSAndrew Gallatin #else
4138fe615baSAndrew Gallatin static void
4146d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev)
415b2fc195eSAndrew Gallatin {
416b2fc195eSAndrew Gallatin 	device_printf(sc->dev,
417b2fc195eSAndrew Gallatin 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4188fe615baSAndrew Gallatin 	return;
419b2fc195eSAndrew Gallatin }
420b2fc195eSAndrew Gallatin #endif
4218fe615baSAndrew Gallatin 
4228fe615baSAndrew Gallatin 
4238fe615baSAndrew Gallatin static int
4248fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type)
4258fe615baSAndrew Gallatin {
4268fe615baSAndrew Gallatin 	mxge_cmd_t cmd;
4278fe615baSAndrew Gallatin 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4288fe615baSAndrew Gallatin 	int status;
4298fe615baSAndrew Gallatin 	uint32_t len;
4308fe615baSAndrew Gallatin 	char *test = " ";
4318fe615baSAndrew Gallatin 
4328fe615baSAndrew Gallatin 
4338fe615baSAndrew Gallatin 	/* Run a small DMA test.
4348fe615baSAndrew Gallatin 	 * The magic multipliers to the length tell the firmware
4358fe615baSAndrew Gallatin 	 * to do DMA read, write, or read+write tests.  The
4368fe615baSAndrew Gallatin 	 * results are returned in cmd.data0.  The upper 16
4378fe615baSAndrew Gallatin 	 * bits of the return is the number of transfers completed.
4388fe615baSAndrew Gallatin 	 * The lower 16 bits is the time in 0.5us ticks that the
4398fe615baSAndrew Gallatin 	 * transfers took to complete.
4408fe615baSAndrew Gallatin 	 */
4418fe615baSAndrew Gallatin 
4428fe615baSAndrew Gallatin 	len = sc->tx.boundary;
4438fe615baSAndrew Gallatin 
4448fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4458fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4468fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10000;
4478fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4488fe615baSAndrew Gallatin 	if (status != 0) {
4498fe615baSAndrew Gallatin 		test = "read";
4508fe615baSAndrew Gallatin 		goto abort;
4518fe615baSAndrew Gallatin 	}
4528fe615baSAndrew Gallatin 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4538fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4548fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4558fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4568fe615baSAndrew Gallatin 	cmd.data2 = len * 0x1;
4578fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4588fe615baSAndrew Gallatin 	if (status != 0) {
4598fe615baSAndrew Gallatin 		test = "write";
4608fe615baSAndrew Gallatin 		goto abort;
4618fe615baSAndrew Gallatin 	}
4628fe615baSAndrew Gallatin 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
4638fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4648fe615baSAndrew Gallatin 
4658fe615baSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4668fe615baSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4678fe615baSAndrew Gallatin 	cmd.data2 = len * 0x10001;
4688fe615baSAndrew Gallatin 	status = mxge_send_cmd(sc, test_type, &cmd);
4698fe615baSAndrew Gallatin 	if (status != 0) {
4708fe615baSAndrew Gallatin 		test = "read/write";
4718fe615baSAndrew Gallatin 		goto abort;
4728fe615baSAndrew Gallatin 	}
4738fe615baSAndrew Gallatin 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4748fe615baSAndrew Gallatin 		(cmd.data0 & 0xffff);
4758fe615baSAndrew Gallatin 
4768fe615baSAndrew Gallatin abort:
4778fe615baSAndrew Gallatin 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
4788fe615baSAndrew Gallatin 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4798fe615baSAndrew Gallatin 			      test, status);
4808fe615baSAndrew Gallatin 
4818fe615baSAndrew Gallatin 	return status;
4828fe615baSAndrew Gallatin }
4838fe615baSAndrew Gallatin 
484b2fc195eSAndrew Gallatin /*
485b2fc195eSAndrew Gallatin  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
486b2fc195eSAndrew Gallatin  * when the PCI-E Completion packets are aligned on an 8-byte
487b2fc195eSAndrew Gallatin  * boundary.  Some PCI-E chip sets always align Completion packets; on
488b2fc195eSAndrew Gallatin  * the ones that do not, the alignment can be enforced by enabling
489b2fc195eSAndrew Gallatin  * ECRC generation (if supported).
490b2fc195eSAndrew Gallatin  *
491b2fc195eSAndrew Gallatin  * When PCI-E Completion packets are not aligned, it is actually more
492b2fc195eSAndrew Gallatin  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
493b2fc195eSAndrew Gallatin  *
494b2fc195eSAndrew Gallatin  * If the driver can neither enable ECRC nor verify that it has
495b2fc195eSAndrew Gallatin  * already been enabled, then it must use a firmware image which works
496b2fc195eSAndrew Gallatin  * around unaligned completion packets (ethp_z8e.dat), and it should
497b2fc195eSAndrew Gallatin  * also ensure that it never gives the device a Read-DMA which is
498b2fc195eSAndrew Gallatin  * larger than 2KB by setting the tx.boundary to 2KB.  If ECRC is
499b2fc195eSAndrew Gallatin  * enabled, then the driver should use the aligned (eth_z8e.dat)
500b2fc195eSAndrew Gallatin  * firmware image, and set tx.boundary to 4KB.
501b2fc195eSAndrew Gallatin  */
502b2fc195eSAndrew Gallatin 
5038fe615baSAndrew Gallatin static int
5048fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc)
5058fe615baSAndrew Gallatin {
5068fe615baSAndrew Gallatin 	device_t dev = sc->dev;
5078fe615baSAndrew Gallatin 	int reg, status;
5088fe615baSAndrew Gallatin 	uint16_t pectl;
5098fe615baSAndrew Gallatin 
5108fe615baSAndrew Gallatin 	sc->tx.boundary = 4096;
5118fe615baSAndrew Gallatin 	/*
5128fe615baSAndrew Gallatin 	 * Verify the max read request size was set to 4KB
5138fe615baSAndrew Gallatin 	 * before trying the test with 4KB.
5148fe615baSAndrew Gallatin 	 */
5158fe615baSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5168fe615baSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
5178fe615baSAndrew Gallatin 		if ((pectl & (5 << 12)) != (5 << 12)) {
5188fe615baSAndrew Gallatin 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5198fe615baSAndrew Gallatin 				      pectl);
5208fe615baSAndrew Gallatin 			sc->tx.boundary = 2048;
5218fe615baSAndrew Gallatin 		}
5228fe615baSAndrew Gallatin 	}
5238fe615baSAndrew Gallatin 
5248fe615baSAndrew Gallatin 	/*
5258fe615baSAndrew Gallatin 	 * load the optimized firmware (which assumes aligned PCIe
5268fe615baSAndrew Gallatin 	 * completions) in order to see if it works on this host.
5278fe615baSAndrew Gallatin 	 */
5288fe615baSAndrew Gallatin 	sc->fw_name = mxge_fw_aligned;
5298fe615baSAndrew Gallatin 	status = mxge_load_firmware(sc);
5308fe615baSAndrew Gallatin 	if (status != 0) {
5318fe615baSAndrew Gallatin 		return status;
5328fe615baSAndrew Gallatin 	}
5338fe615baSAndrew Gallatin 
5348fe615baSAndrew Gallatin 	/*
5358fe615baSAndrew Gallatin 	 * Enable ECRC if possible
5368fe615baSAndrew Gallatin 	 */
5378fe615baSAndrew Gallatin 	mxge_enable_nvidia_ecrc(sc);
5388fe615baSAndrew Gallatin 
5398fe615baSAndrew Gallatin 	/*
5408fe615baSAndrew Gallatin 	 * Run a DMA test which watches for unaligned completions and
5418fe615baSAndrew Gallatin 	 * aborts on the first one seen.
5428fe615baSAndrew Gallatin 	 */
5438fe615baSAndrew Gallatin 
5448fe615baSAndrew Gallatin 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5458fe615baSAndrew Gallatin 	if (status == 0)
5468fe615baSAndrew Gallatin 		return 0; /* keep the aligned firmware */
5478fe615baSAndrew Gallatin 
5488fe615baSAndrew Gallatin 	if (status != E2BIG)
5498fe615baSAndrew Gallatin 		device_printf(dev, "DMA test failed: %d\n", status);
5508fe615baSAndrew Gallatin 	if (status == ENOSYS)
5518fe615baSAndrew Gallatin 		device_printf(dev, "Falling back to ethp! "
5528fe615baSAndrew Gallatin 			      "Please install up to date fw\n");
5538fe615baSAndrew Gallatin 	return status;
5548fe615baSAndrew Gallatin }
5558fe615baSAndrew Gallatin 
5568fe615baSAndrew Gallatin static int
5576d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc)
558b2fc195eSAndrew Gallatin {
5598fe615baSAndrew Gallatin 	int aligned = 0;
560b2fc195eSAndrew Gallatin 
561d91b1b49SAndrew Gallatin 
562d91b1b49SAndrew Gallatin 	if (mxge_force_firmware != 0) {
563d91b1b49SAndrew Gallatin 		if (mxge_force_firmware == 1)
564d91b1b49SAndrew Gallatin 			aligned = 1;
565d91b1b49SAndrew Gallatin 		else
566d91b1b49SAndrew Gallatin 			aligned = 0;
567d91b1b49SAndrew Gallatin 		if (mxge_verbose)
568d91b1b49SAndrew Gallatin 			device_printf(sc->dev,
569d91b1b49SAndrew Gallatin 				      "Assuming %s completions (forced)\n",
570d91b1b49SAndrew Gallatin 				      aligned ? "aligned" : "unaligned");
571d91b1b49SAndrew Gallatin 		goto abort;
572d91b1b49SAndrew Gallatin 	}
573d91b1b49SAndrew Gallatin 
574d91b1b49SAndrew Gallatin 	/* if the PCIe link width is 4 or less, we can use the aligned
575d91b1b49SAndrew Gallatin 	   firmware and skip any checks */
576d91b1b49SAndrew Gallatin 	if (sc->link_width != 0 && sc->link_width <= 4) {
577d91b1b49SAndrew Gallatin 		device_printf(sc->dev,
578d91b1b49SAndrew Gallatin 			      "PCIe x%d Link, expect reduced performance\n",
579d91b1b49SAndrew Gallatin 			      sc->link_width);
580d91b1b49SAndrew Gallatin 		aligned = 1;
581d91b1b49SAndrew Gallatin 		goto abort;
582d91b1b49SAndrew Gallatin 	}
583d91b1b49SAndrew Gallatin 
5848fe615baSAndrew Gallatin 	if (0 == mxge_firmware_probe(sc))
5858fe615baSAndrew Gallatin 		return 0;
586b2fc195eSAndrew Gallatin 
587b2fc195eSAndrew Gallatin abort:
588b2fc195eSAndrew Gallatin 	if (aligned) {
5896d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_aligned;
590b2fc195eSAndrew Gallatin 		sc->tx.boundary = 4096;
591b2fc195eSAndrew Gallatin 	} else {
5926d87a65dSAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
593b2fc195eSAndrew Gallatin 		sc->tx.boundary = 2048;
594b2fc195eSAndrew Gallatin 	}
5958fe615baSAndrew Gallatin 	return (mxge_load_firmware(sc));
596b2fc195eSAndrew Gallatin }
597b2fc195eSAndrew Gallatin 
598b2fc195eSAndrew Gallatin union qualhack
599b2fc195eSAndrew Gallatin {
600b2fc195eSAndrew Gallatin         const char *ro_char;
601b2fc195eSAndrew Gallatin         char *rw_char;
602b2fc195eSAndrew Gallatin };
603b2fc195eSAndrew Gallatin 
6044da0d523SAndrew Gallatin static int
6054da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6064da0d523SAndrew Gallatin {
607b824b7d8SAndrew Gallatin 
6084da0d523SAndrew Gallatin 
6094da0d523SAndrew Gallatin 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6104da0d523SAndrew Gallatin 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6114da0d523SAndrew Gallatin 			      be32toh(hdr->mcp_type));
6124da0d523SAndrew Gallatin 		return EIO;
6134da0d523SAndrew Gallatin 	}
6144da0d523SAndrew Gallatin 
6154da0d523SAndrew Gallatin 	/* save firmware version for sysctl */
6164da0d523SAndrew Gallatin 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6174da0d523SAndrew Gallatin 	if (mxge_verbose)
6184da0d523SAndrew Gallatin 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6194da0d523SAndrew Gallatin 
620b824b7d8SAndrew Gallatin 	sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
621b824b7d8SAndrew Gallatin 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6224da0d523SAndrew Gallatin 
623b824b7d8SAndrew Gallatin 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
624b824b7d8SAndrew Gallatin 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6254da0d523SAndrew Gallatin 		device_printf(sc->dev, "Found firmware version %s\n",
6264da0d523SAndrew Gallatin 			      sc->fw_version);
6274da0d523SAndrew Gallatin 		device_printf(sc->dev, "Driver needs %d.%d\n",
6284da0d523SAndrew Gallatin 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6294da0d523SAndrew Gallatin 		return EINVAL;
6304da0d523SAndrew Gallatin 	}
6314da0d523SAndrew Gallatin 	return 0;
6324da0d523SAndrew Gallatin 
6334da0d523SAndrew Gallatin }
634b2fc195eSAndrew Gallatin 
635b2fc195eSAndrew Gallatin static int
6366d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
637b2fc195eSAndrew Gallatin {
63833d54970SLuigi Rizzo 	const struct firmware *fw;
639b2fc195eSAndrew Gallatin 	const mcp_gen_header_t *hdr;
640b2fc195eSAndrew Gallatin 	unsigned hdr_offset;
641b2fc195eSAndrew Gallatin 	const char *fw_data;
642b2fc195eSAndrew Gallatin 	union qualhack hack;
643b2fc195eSAndrew Gallatin 	int status;
6444da0d523SAndrew Gallatin 	unsigned int i;
6454da0d523SAndrew Gallatin 	char dummy;
646b2fc195eSAndrew Gallatin 
647b2fc195eSAndrew Gallatin 
648b2fc195eSAndrew Gallatin 	fw = firmware_get(sc->fw_name);
649b2fc195eSAndrew Gallatin 
650b2fc195eSAndrew Gallatin 	if (fw == NULL) {
651b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Could not find firmware image %s\n",
652b2fc195eSAndrew Gallatin 			      sc->fw_name);
653b2fc195eSAndrew Gallatin 		return ENOENT;
654b2fc195eSAndrew Gallatin 	}
655b2fc195eSAndrew Gallatin 	if (fw->datasize > *limit ||
656b2fc195eSAndrew Gallatin 	    fw->datasize < MCP_HEADER_PTR_OFFSET + 4) {
657b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n",
658b2fc195eSAndrew Gallatin 			      sc->fw_name, (int)fw->datasize, (int) *limit);
659b2fc195eSAndrew Gallatin 		status = ENOSPC;
660b2fc195eSAndrew Gallatin 		goto abort_with_fw;
661b2fc195eSAndrew Gallatin 	}
662b2fc195eSAndrew Gallatin 	*limit = fw->datasize;
663b2fc195eSAndrew Gallatin 
664b2fc195eSAndrew Gallatin 	/* check id */
665b2fc195eSAndrew Gallatin 	fw_data = (const char *)fw->data;
666b2fc195eSAndrew Gallatin 	hdr_offset = htobe32(*(const uint32_t *)
667b2fc195eSAndrew Gallatin 			     (fw_data + MCP_HEADER_PTR_OFFSET));
668b2fc195eSAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) {
669b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Bad firmware file");
670b2fc195eSAndrew Gallatin 		status = EIO;
671b2fc195eSAndrew Gallatin 		goto abort_with_fw;
672b2fc195eSAndrew Gallatin 	}
673b2fc195eSAndrew Gallatin 	hdr = (const void*)(fw_data + hdr_offset);
674b2fc195eSAndrew Gallatin 
6754da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
6764da0d523SAndrew Gallatin 	if (status != 0)
6774da0d523SAndrew Gallatin 		goto abort_with_fw;
678b2fc195eSAndrew Gallatin 
679b2fc195eSAndrew Gallatin 	hack.ro_char = fw_data;
680b2fc195eSAndrew Gallatin 	/* Copy the inflated firmware to NIC SRAM. */
6814da0d523SAndrew Gallatin 	for (i = 0; i < *limit; i += 256) {
6824da0d523SAndrew Gallatin 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
6834da0d523SAndrew Gallatin 			      hack.rw_char + i,
6844da0d523SAndrew Gallatin 			      min(256U, (unsigned)(*limit - i)));
6854da0d523SAndrew Gallatin 		mb();
6864da0d523SAndrew Gallatin 		dummy = *sc->sram;
6874da0d523SAndrew Gallatin 		mb();
6884da0d523SAndrew Gallatin 	}
689b2fc195eSAndrew Gallatin 
690b2fc195eSAndrew Gallatin 	status = 0;
691b2fc195eSAndrew Gallatin abort_with_fw:
692b2fc195eSAndrew Gallatin 	firmware_put(fw, FIRMWARE_UNLOAD);
693b2fc195eSAndrew Gallatin 	return status;
694b2fc195eSAndrew Gallatin }
695b2fc195eSAndrew Gallatin 
696b2fc195eSAndrew Gallatin /*
697b2fc195eSAndrew Gallatin  * Enable or disable periodic RDMAs from the host to make certain
698b2fc195eSAndrew Gallatin  * chipsets resend dropped PCIe messages
699b2fc195eSAndrew Gallatin  */
700b2fc195eSAndrew Gallatin 
701b2fc195eSAndrew Gallatin static void
7026d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable)
703b2fc195eSAndrew Gallatin {
704b2fc195eSAndrew Gallatin 	char buf_bytes[72];
705b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
706b2fc195eSAndrew Gallatin 	volatile char *submit;
707b2fc195eSAndrew Gallatin 	uint32_t *buf, dma_low, dma_high;
708b2fc195eSAndrew Gallatin 	int i;
709b2fc195eSAndrew Gallatin 
710b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
711b2fc195eSAndrew Gallatin 
712b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
713b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
714b2fc195eSAndrew Gallatin 	*confirm = 0;
715b2fc195eSAndrew Gallatin 	mb();
716b2fc195eSAndrew Gallatin 
717b2fc195eSAndrew Gallatin 	/* send an rdma command to the PCIe engine, and wait for the
718b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
719b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
720b2fc195eSAndrew Gallatin 	*/
721b2fc195eSAndrew Gallatin 
7226d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7236d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
724b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
725b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
726b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7276d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
7286d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
729b2fc195eSAndrew Gallatin 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
730b2fc195eSAndrew Gallatin 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
731b2fc195eSAndrew Gallatin 	buf[5] = htobe32(enable);			/* enable? */
732b2fc195eSAndrew Gallatin 
733b2fc195eSAndrew Gallatin 
7340fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
735b2fc195eSAndrew Gallatin 
7366d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
737b2fc195eSAndrew Gallatin 	mb();
738b2fc195eSAndrew Gallatin 	DELAY(1000);
739b2fc195eSAndrew Gallatin 	mb();
740b2fc195eSAndrew Gallatin 	i = 0;
741b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
742b2fc195eSAndrew Gallatin 		DELAY(1000);
743b2fc195eSAndrew Gallatin 		i++;
744b2fc195eSAndrew Gallatin 	}
745b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
746b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
747b2fc195eSAndrew Gallatin 			      (enable ? "enable" : "disable"), confirm,
748b2fc195eSAndrew Gallatin 			      *confirm);
749b2fc195eSAndrew Gallatin 	}
750b2fc195eSAndrew Gallatin 	return;
751b2fc195eSAndrew Gallatin }
752b2fc195eSAndrew Gallatin 
753b2fc195eSAndrew Gallatin static int
7546d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
755b2fc195eSAndrew Gallatin {
756b2fc195eSAndrew Gallatin 	mcp_cmd_t *buf;
757b2fc195eSAndrew Gallatin 	char buf_bytes[sizeof(*buf) + 8];
758b2fc195eSAndrew Gallatin 	volatile mcp_cmd_response_t *response = sc->cmd;
7590fa7f681SAndrew Gallatin 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
760b2fc195eSAndrew Gallatin 	uint32_t dma_low, dma_high;
761e0501fd0SAndrew Gallatin 	int err, sleep_total = 0;
762b2fc195eSAndrew Gallatin 
763b2fc195eSAndrew Gallatin 	/* ensure buf is aligned to 8 bytes */
764b2fc195eSAndrew Gallatin 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
765b2fc195eSAndrew Gallatin 
766b2fc195eSAndrew Gallatin 	buf->data0 = htobe32(data->data0);
767b2fc195eSAndrew Gallatin 	buf->data1 = htobe32(data->data1);
768b2fc195eSAndrew Gallatin 	buf->data2 = htobe32(data->data2);
769b2fc195eSAndrew Gallatin 	buf->cmd = htobe32(cmd);
7706d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7716d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
772b2fc195eSAndrew Gallatin 
773b2fc195eSAndrew Gallatin 	buf->response_addr.low = htobe32(dma_low);
774b2fc195eSAndrew Gallatin 	buf->response_addr.high = htobe32(dma_high);
775a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->cmd_mtx);
776b2fc195eSAndrew Gallatin 	response->result = 0xffffffff;
777b2fc195eSAndrew Gallatin 	mb();
7786d87a65dSAndrew Gallatin 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
779b2fc195eSAndrew Gallatin 
7805e7d8541SAndrew Gallatin 	/* wait up to 20ms */
781e0501fd0SAndrew Gallatin 	err = EAGAIN;
7825e7d8541SAndrew Gallatin 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
783b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
784b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
785b2fc195eSAndrew Gallatin 		mb();
786e0501fd0SAndrew Gallatin 		switch (be32toh(response->result)) {
787e0501fd0SAndrew Gallatin 		case 0:
788b2fc195eSAndrew Gallatin 			data->data0 = be32toh(response->data);
789e0501fd0SAndrew Gallatin 			err = 0;
790e0501fd0SAndrew Gallatin 			break;
791e0501fd0SAndrew Gallatin 		case 0xffffffff:
792e0501fd0SAndrew Gallatin 			DELAY(1000);
793e0501fd0SAndrew Gallatin 			break;
794e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_UNKNOWN:
795e0501fd0SAndrew Gallatin 			err = ENOSYS;
796e0501fd0SAndrew Gallatin 			break;
797e0501fd0SAndrew Gallatin 		case MXGEFW_CMD_ERROR_UNALIGNED:
798e0501fd0SAndrew Gallatin 			err = E2BIG;
799e0501fd0SAndrew Gallatin 			break;
800e0501fd0SAndrew Gallatin 		default:
801b2fc195eSAndrew Gallatin 			device_printf(sc->dev,
8026d87a65dSAndrew Gallatin 				      "mxge: command %d "
803b2fc195eSAndrew Gallatin 				      "failed, result = %d\n",
804b2fc195eSAndrew Gallatin 				      cmd, be32toh(response->result));
805e0501fd0SAndrew Gallatin 			err = ENXIO;
806e0501fd0SAndrew Gallatin 			break;
807b2fc195eSAndrew Gallatin 		}
808e0501fd0SAndrew Gallatin 		if (err != EAGAIN)
809e0501fd0SAndrew Gallatin 			break;
810b2fc195eSAndrew Gallatin 	}
811e0501fd0SAndrew Gallatin 	if (err == EAGAIN)
8126d87a65dSAndrew Gallatin 		device_printf(sc->dev, "mxge: command %d timed out"
813b2fc195eSAndrew Gallatin 			      "result = %d\n",
814b2fc195eSAndrew Gallatin 			      cmd, be32toh(response->result));
815e0501fd0SAndrew Gallatin 	mtx_unlock(&sc->cmd_mtx);
816e0501fd0SAndrew Gallatin 	return err;
817b2fc195eSAndrew Gallatin }
818b2fc195eSAndrew Gallatin 
8194da0d523SAndrew Gallatin static int
8204da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc)
8214da0d523SAndrew Gallatin {
8224da0d523SAndrew Gallatin 	struct mcp_gen_header *hdr;
8234da0d523SAndrew Gallatin 	const size_t bytes = sizeof (struct mcp_gen_header);
8244da0d523SAndrew Gallatin 	size_t hdr_offset;
8254da0d523SAndrew Gallatin 	int status;
8264da0d523SAndrew Gallatin 
8274da0d523SAndrew Gallatin 	/* find running firmware header */
8284da0d523SAndrew Gallatin 	hdr_offset = htobe32(*(volatile uint32_t *)
8294da0d523SAndrew Gallatin 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
8304da0d523SAndrew Gallatin 
8314da0d523SAndrew Gallatin 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8324da0d523SAndrew Gallatin 		device_printf(sc->dev,
8334da0d523SAndrew Gallatin 			      "Running firmware has bad header offset (%d)\n",
8344da0d523SAndrew Gallatin 			      (int)hdr_offset);
8354da0d523SAndrew Gallatin 		return EIO;
8364da0d523SAndrew Gallatin 	}
8374da0d523SAndrew Gallatin 
8384da0d523SAndrew Gallatin 	/* copy header of running firmware from SRAM to host memory to
8394da0d523SAndrew Gallatin 	 * validate firmware */
8404da0d523SAndrew Gallatin 	hdr = malloc(bytes, M_DEVBUF, M_NOWAIT);
8414da0d523SAndrew Gallatin 	if (hdr == NULL) {
8424da0d523SAndrew Gallatin 		device_printf(sc->dev, "could not malloc firmware hdr\n");
8434da0d523SAndrew Gallatin 		return ENOMEM;
8444da0d523SAndrew Gallatin 	}
8454da0d523SAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8464da0d523SAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
8474da0d523SAndrew Gallatin 				hdr_offset, (char *)hdr, bytes);
8484da0d523SAndrew Gallatin 	status = mxge_validate_firmware(sc, hdr);
8494da0d523SAndrew Gallatin 	free(hdr, M_DEVBUF);
850b824b7d8SAndrew Gallatin 
851b824b7d8SAndrew Gallatin 	/*
852b824b7d8SAndrew Gallatin 	 * check to see if adopted firmware has bug where adopting
853b824b7d8SAndrew Gallatin 	 * it will cause broadcasts to be filtered unless the NIC
854b824b7d8SAndrew Gallatin 	 * is kept in ALLMULTI mode
855b824b7d8SAndrew Gallatin 	 */
856b824b7d8SAndrew Gallatin 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
857b824b7d8SAndrew Gallatin 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
858b824b7d8SAndrew Gallatin 		sc->adopted_rx_filter_bug = 1;
859b824b7d8SAndrew Gallatin 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
860b824b7d8SAndrew Gallatin 			      "working around rx filter bug\n",
861b824b7d8SAndrew Gallatin 			      sc->fw_ver_major, sc->fw_ver_minor,
862b824b7d8SAndrew Gallatin 			      sc->fw_ver_tiny);
863b824b7d8SAndrew Gallatin 	}
864b824b7d8SAndrew Gallatin 
8654da0d523SAndrew Gallatin 	return status;
8664da0d523SAndrew Gallatin }
8674da0d523SAndrew Gallatin 
868b2fc195eSAndrew Gallatin 
869b2fc195eSAndrew Gallatin static int
8706d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc)
871b2fc195eSAndrew Gallatin {
872b2fc195eSAndrew Gallatin 	volatile uint32_t *confirm;
873b2fc195eSAndrew Gallatin 	volatile char *submit;
874b2fc195eSAndrew Gallatin 	char buf_bytes[72];
875b2fc195eSAndrew Gallatin 	uint32_t *buf, size, dma_low, dma_high;
876b2fc195eSAndrew Gallatin 	int status, i;
877b2fc195eSAndrew Gallatin 
878b2fc195eSAndrew Gallatin 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
879b2fc195eSAndrew Gallatin 
880b2fc195eSAndrew Gallatin 	size = sc->sram_size;
8816d87a65dSAndrew Gallatin 	status = mxge_load_firmware_helper(sc, &size);
882b2fc195eSAndrew Gallatin 	if (status) {
8834da0d523SAndrew Gallatin 		/* Try to use the currently running firmware, if
8844da0d523SAndrew Gallatin 		   it is new enough */
8854da0d523SAndrew Gallatin 		status = mxge_adopt_running_firmware(sc);
8864da0d523SAndrew Gallatin 		if (status) {
8874da0d523SAndrew Gallatin 			device_printf(sc->dev,
8884da0d523SAndrew Gallatin 				      "failed to adopt running firmware\n");
889b2fc195eSAndrew Gallatin 			return status;
890b2fc195eSAndrew Gallatin 		}
8914da0d523SAndrew Gallatin 		device_printf(sc->dev,
8924da0d523SAndrew Gallatin 			      "Successfully adopted running firmware\n");
8934da0d523SAndrew Gallatin 		if (sc->tx.boundary == 4096) {
8944da0d523SAndrew Gallatin 			device_printf(sc->dev,
8954da0d523SAndrew Gallatin 				"Using firmware currently running on NIC"
8964da0d523SAndrew Gallatin 				 ".  For optimal\n");
8974da0d523SAndrew Gallatin 			device_printf(sc->dev,
8984da0d523SAndrew Gallatin 				 "performance consider loading optimized "
8994da0d523SAndrew Gallatin 				 "firmware\n");
9004da0d523SAndrew Gallatin 		}
901d91b1b49SAndrew Gallatin 		sc->fw_name = mxge_fw_unaligned;
902d91b1b49SAndrew Gallatin 		sc->tx.boundary = 2048;
903d91b1b49SAndrew Gallatin 		return 0;
9044da0d523SAndrew Gallatin 	}
905b2fc195eSAndrew Gallatin 	/* clear confirmation addr */
906b2fc195eSAndrew Gallatin 	confirm = (volatile uint32_t *)sc->cmd;
907b2fc195eSAndrew Gallatin 	*confirm = 0;
908b2fc195eSAndrew Gallatin 	mb();
909b2fc195eSAndrew Gallatin 	/* send a reload command to the bootstrap MCP, and wait for the
910b2fc195eSAndrew Gallatin 	   response in the confirmation address.  The firmware should
911b2fc195eSAndrew Gallatin 	   write a -1 there to indicate it is alive and well
912b2fc195eSAndrew Gallatin 	*/
913b2fc195eSAndrew Gallatin 
9146d87a65dSAndrew Gallatin 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9156d87a65dSAndrew Gallatin 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
916b2fc195eSAndrew Gallatin 
917b2fc195eSAndrew Gallatin 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
918b2fc195eSAndrew Gallatin 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
919b2fc195eSAndrew Gallatin 	buf[2] = htobe32(0xffffffff);	/* confirm data */
920b2fc195eSAndrew Gallatin 
921b2fc195eSAndrew Gallatin 	/* FIX: All newest firmware should un-protect the bottom of
922b2fc195eSAndrew Gallatin 	   the sram before handoff. However, the very first interfaces
923b2fc195eSAndrew Gallatin 	   do not. Therefore the handoff copy must skip the first 8 bytes
924b2fc195eSAndrew Gallatin 	*/
925b2fc195eSAndrew Gallatin 					/* where the code starts*/
9266d87a65dSAndrew Gallatin 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
927b2fc195eSAndrew Gallatin 	buf[4] = htobe32(size - 8); 	/* length of code */
928b2fc195eSAndrew Gallatin 	buf[5] = htobe32(8);		/* where to copy to */
929b2fc195eSAndrew Gallatin 	buf[6] = htobe32(0);		/* where to jump to */
930b2fc195eSAndrew Gallatin 
9310fa7f681SAndrew Gallatin 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9326d87a65dSAndrew Gallatin 	mxge_pio_copy(submit, buf, 64);
933b2fc195eSAndrew Gallatin 	mb();
934b2fc195eSAndrew Gallatin 	DELAY(1000);
935b2fc195eSAndrew Gallatin 	mb();
936b2fc195eSAndrew Gallatin 	i = 0;
937b2fc195eSAndrew Gallatin 	while (*confirm != 0xffffffff && i < 20) {
938b2fc195eSAndrew Gallatin 		DELAY(1000*10);
939b2fc195eSAndrew Gallatin 		i++;
940b2fc195eSAndrew Gallatin 		bus_dmamap_sync(sc->cmd_dma.dmat,
941b2fc195eSAndrew Gallatin 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
942b2fc195eSAndrew Gallatin 	}
943b2fc195eSAndrew Gallatin 	if (*confirm != 0xffffffff) {
944b2fc195eSAndrew Gallatin 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
945b2fc195eSAndrew Gallatin 			confirm, *confirm);
946b2fc195eSAndrew Gallatin 
947b2fc195eSAndrew Gallatin 		return ENXIO;
948b2fc195eSAndrew Gallatin 	}
949b2fc195eSAndrew Gallatin 	return 0;
950b2fc195eSAndrew Gallatin }
951b2fc195eSAndrew Gallatin 
952b2fc195eSAndrew Gallatin static int
9536d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc)
954b2fc195eSAndrew Gallatin {
9556d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
956b2fc195eSAndrew Gallatin 	uint8_t *addr = sc->mac_addr;
957b2fc195eSAndrew Gallatin 	int status;
958b2fc195eSAndrew Gallatin 
959b2fc195eSAndrew Gallatin 
960b2fc195eSAndrew Gallatin 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
961b2fc195eSAndrew Gallatin 		     | (addr[2] << 8) | addr[3]);
962b2fc195eSAndrew Gallatin 
963b2fc195eSAndrew Gallatin 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
964b2fc195eSAndrew Gallatin 
9655e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
966b2fc195eSAndrew Gallatin 	return status;
967b2fc195eSAndrew Gallatin }
968b2fc195eSAndrew Gallatin 
969b2fc195eSAndrew Gallatin static int
9706d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause)
971b2fc195eSAndrew Gallatin {
9726d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
973b2fc195eSAndrew Gallatin 	int status;
974b2fc195eSAndrew Gallatin 
975b2fc195eSAndrew Gallatin 	if (pause)
9765e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
977b2fc195eSAndrew Gallatin 				       &cmd);
978b2fc195eSAndrew Gallatin 	else
9795e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
980b2fc195eSAndrew Gallatin 				       &cmd);
981b2fc195eSAndrew Gallatin 
982b2fc195eSAndrew Gallatin 	if (status) {
983b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set flow control mode\n");
984b2fc195eSAndrew Gallatin 		return ENXIO;
985b2fc195eSAndrew Gallatin 	}
986b2fc195eSAndrew Gallatin 	sc->pause = pause;
987b2fc195eSAndrew Gallatin 	return 0;
988b2fc195eSAndrew Gallatin }
989b2fc195eSAndrew Gallatin 
990b2fc195eSAndrew Gallatin static void
9916d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc)
992b2fc195eSAndrew Gallatin {
9936d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
994b2fc195eSAndrew Gallatin 	int status;
995b2fc195eSAndrew Gallatin 
996b2fc195eSAndrew Gallatin 	if (promisc)
9975e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
998b2fc195eSAndrew Gallatin 				       &cmd);
999b2fc195eSAndrew Gallatin 	else
10005e7d8541SAndrew Gallatin 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
1001b2fc195eSAndrew Gallatin 				       &cmd);
1002b2fc195eSAndrew Gallatin 
1003b2fc195eSAndrew Gallatin 	if (status) {
1004b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Failed to set promisc mode\n");
1005b2fc195eSAndrew Gallatin 	}
1006b2fc195eSAndrew Gallatin }
1007b2fc195eSAndrew Gallatin 
10080fa7f681SAndrew Gallatin static void
10090fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc)
10100fa7f681SAndrew Gallatin {
10110fa7f681SAndrew Gallatin 	mxge_cmd_t cmd;
10120fa7f681SAndrew Gallatin 	struct ifmultiaddr *ifma;
10130fa7f681SAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
10140fa7f681SAndrew Gallatin 	int err;
10150fa7f681SAndrew Gallatin 
10160fa7f681SAndrew Gallatin 	/* This firmware is known to not support multicast */
10170fa7f681SAndrew Gallatin 	if (!sc->fw_multicast_support)
10180fa7f681SAndrew Gallatin 		return;
10190fa7f681SAndrew Gallatin 
10200fa7f681SAndrew Gallatin 	/* Disable multicast filtering while we play with the lists*/
10210fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10220fa7f681SAndrew Gallatin 	if (err != 0) {
10230fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
10240fa7f681SAndrew Gallatin 		       " error status: %d\n", err);
10250fa7f681SAndrew Gallatin 		return;
10260fa7f681SAndrew Gallatin 	}
10270fa7f681SAndrew Gallatin 
1028b824b7d8SAndrew Gallatin 	if (sc->adopted_rx_filter_bug)
1029b824b7d8SAndrew Gallatin 		return;
10300fa7f681SAndrew Gallatin 
10310fa7f681SAndrew Gallatin 	if (ifp->if_flags & IFF_ALLMULTI)
10320fa7f681SAndrew Gallatin 		/* request to disable multicast filtering, so quit here */
10330fa7f681SAndrew Gallatin 		return;
10340fa7f681SAndrew Gallatin 
10350fa7f681SAndrew Gallatin 	/* Flush all the filters */
10360fa7f681SAndrew Gallatin 
10370fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10380fa7f681SAndrew Gallatin 	if (err != 0) {
10390fa7f681SAndrew Gallatin 		device_printf(sc->dev,
10400fa7f681SAndrew Gallatin 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
10410fa7f681SAndrew Gallatin 			      ", error status: %d\n", err);
10420fa7f681SAndrew Gallatin 		return;
10430fa7f681SAndrew Gallatin 	}
10440fa7f681SAndrew Gallatin 
10450fa7f681SAndrew Gallatin 	/* Walk the multicast list, and add each address */
10460fa7f681SAndrew Gallatin 
10470fa7f681SAndrew Gallatin 	IF_ADDR_LOCK(ifp);
10480fa7f681SAndrew Gallatin 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10490fa7f681SAndrew Gallatin 		if (ifma->ifma_addr->sa_family != AF_LINK)
10500fa7f681SAndrew Gallatin 			continue;
10510fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10520fa7f681SAndrew Gallatin 		      &cmd.data0, 4);
10530fa7f681SAndrew Gallatin 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10540fa7f681SAndrew Gallatin 		      &cmd.data1, 2);
10550fa7f681SAndrew Gallatin 		cmd.data0 = htonl(cmd.data0);
10560fa7f681SAndrew Gallatin 		cmd.data1 = htonl(cmd.data1);
10570fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10580fa7f681SAndrew Gallatin 		if (err != 0) {
10590fa7f681SAndrew Gallatin 			device_printf(sc->dev, "Failed "
10600fa7f681SAndrew Gallatin 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
10610fa7f681SAndrew Gallatin 			       "%d\t", err);
10620fa7f681SAndrew Gallatin 			/* abort, leaving multicast filtering off */
10630fa7f681SAndrew Gallatin 			IF_ADDR_UNLOCK(ifp);
10640fa7f681SAndrew Gallatin 			return;
10650fa7f681SAndrew Gallatin 		}
10660fa7f681SAndrew Gallatin 	}
10670fa7f681SAndrew Gallatin 	IF_ADDR_UNLOCK(ifp);
10680fa7f681SAndrew Gallatin 	/* Enable multicast filtering */
10690fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10700fa7f681SAndrew Gallatin 	if (err != 0) {
10710fa7f681SAndrew Gallatin 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
10720fa7f681SAndrew Gallatin 		       ", error status: %d\n", err);
10730fa7f681SAndrew Gallatin 	}
10740fa7f681SAndrew Gallatin }
10750fa7f681SAndrew Gallatin 
1076b2fc195eSAndrew Gallatin static int
1077053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc)
1078053e637fSAndrew Gallatin {
1079053e637fSAndrew Gallatin 	mxge_cmd_t cmd;
1080053e637fSAndrew Gallatin 	int status;
1081053e637fSAndrew Gallatin 
1082053e637fSAndrew Gallatin 	if (MJUMPAGESIZE - MXGEFW_PAD > MXGE_MAX_ETHER_MTU)
1083053e637fSAndrew Gallatin 		return MXGE_MAX_ETHER_MTU - MXGEFW_PAD;
1084053e637fSAndrew Gallatin 
1085053e637fSAndrew Gallatin 	/* try to set nbufs to see if it we can
1086053e637fSAndrew Gallatin 	   use virtually contiguous jumbos */
1087053e637fSAndrew Gallatin 	cmd.data0 = 0;
1088053e637fSAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
1089053e637fSAndrew Gallatin 			       &cmd);
1090053e637fSAndrew Gallatin 	if (status == 0)
1091053e637fSAndrew Gallatin 		return MXGE_MAX_ETHER_MTU - MXGEFW_PAD;
1092053e637fSAndrew Gallatin 
1093053e637fSAndrew Gallatin 	/* otherwise, we're limited to MJUMPAGESIZE */
1094053e637fSAndrew Gallatin 	return MJUMPAGESIZE - MXGEFW_PAD;
1095053e637fSAndrew Gallatin }
1096053e637fSAndrew Gallatin 
1097053e637fSAndrew Gallatin static int
10986d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc)
1099b2fc195eSAndrew Gallatin {
1100b2fc195eSAndrew Gallatin 
11016d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
11025e7d8541SAndrew Gallatin 	size_t bytes;
11035e7d8541SAndrew Gallatin 	int status;
1104b2fc195eSAndrew Gallatin 
1105b2fc195eSAndrew Gallatin 	/* try to send a reset command to the card to see if it
1106b2fc195eSAndrew Gallatin 	   is alive */
1107b2fc195eSAndrew Gallatin 	memset(&cmd, 0, sizeof (cmd));
11085e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
1109b2fc195eSAndrew Gallatin 	if (status != 0) {
1110b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed reset\n");
1111b2fc195eSAndrew Gallatin 		return ENXIO;
1112b2fc195eSAndrew Gallatin 	}
1113b2fc195eSAndrew Gallatin 
1114091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 1);
1115091feecdSAndrew Gallatin 
1116b2fc195eSAndrew Gallatin 	/* Now exchange information about interrupts  */
11175e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\
11185e7d8541SAndrew Gallatin 	memset(sc->rx_done.entry, 0, bytes);
11195e7d8541SAndrew Gallatin 	cmd.data0 = (uint32_t)bytes;
11205e7d8541SAndrew Gallatin 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11215e7d8541SAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr);
11225e7d8541SAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr);
11235e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd);
1124b2fc195eSAndrew Gallatin 
11256d87a65dSAndrew Gallatin 	status |= mxge_send_cmd(sc,
11265e7d8541SAndrew Gallatin 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
11275e7d8541SAndrew Gallatin 
11285e7d8541SAndrew Gallatin 
11295e7d8541SAndrew Gallatin 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11305e7d8541SAndrew Gallatin 
11315e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11325e7d8541SAndrew Gallatin 	sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11335e7d8541SAndrew Gallatin 
11345e7d8541SAndrew Gallatin 
11355e7d8541SAndrew Gallatin 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
11366d87a65dSAndrew Gallatin 				&cmd);
11375e7d8541SAndrew Gallatin 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
1138b2fc195eSAndrew Gallatin 	if (status != 0) {
1139b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed set interrupt parameters\n");
1140b2fc195eSAndrew Gallatin 		return status;
1141b2fc195eSAndrew Gallatin 	}
1142b2fc195eSAndrew Gallatin 
11435e7d8541SAndrew Gallatin 
11445e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11455e7d8541SAndrew Gallatin 
11465e7d8541SAndrew Gallatin 
11475e7d8541SAndrew Gallatin 	/* run a DMA benchmark */
11488fe615baSAndrew Gallatin 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
11495e7d8541SAndrew Gallatin 
1150b2fc195eSAndrew Gallatin 	/* reset mcp/driver shared state back to 0 */
11515e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
11525e7d8541SAndrew Gallatin 	sc->rx_done.idx = 0;
11535e7d8541SAndrew Gallatin 	sc->rx_done.cnt = 0;
1154b2fc195eSAndrew Gallatin 	sc->tx.req = 0;
1155b2fc195eSAndrew Gallatin 	sc->tx.done = 0;
11565e7d8541SAndrew Gallatin 	sc->tx.pkt_done = 0;
1157a82c2581SAndrew Gallatin 	sc->tx.wake = 0;
1158a82c2581SAndrew Gallatin 	sc->tx.stall = 0;
1159b2fc195eSAndrew Gallatin 	sc->rx_big.cnt = 0;
1160b2fc195eSAndrew Gallatin 	sc->rx_small.cnt = 0;
1161b2fc195eSAndrew Gallatin 	sc->rdma_tags_available = 15;
1162a98d6cd7SAndrew Gallatin 	sc->fw_stats->valid = 0;
1163a98d6cd7SAndrew Gallatin 	sc->fw_stats->send_done_count = 0;
1164053e637fSAndrew Gallatin 	sc->lro_bad_csum = 0;
1165053e637fSAndrew Gallatin 	sc->lro_queued = 0;
1166053e637fSAndrew Gallatin 	sc->lro_flushed = 0;
11676d87a65dSAndrew Gallatin 	status = mxge_update_mac_address(sc);
11686d87a65dSAndrew Gallatin 	mxge_change_promisc(sc, 0);
11696d87a65dSAndrew Gallatin 	mxge_change_pause(sc, sc->pause);
11700fa7f681SAndrew Gallatin 	mxge_set_multicast_list(sc);
1171b2fc195eSAndrew Gallatin 	return status;
1172b2fc195eSAndrew Gallatin }
1173b2fc195eSAndrew Gallatin 
1174b2fc195eSAndrew Gallatin static int
11756d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
1176b2fc195eSAndrew Gallatin {
11776d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1178b2fc195eSAndrew Gallatin         unsigned int intr_coal_delay;
1179b2fc195eSAndrew Gallatin         int err;
1180b2fc195eSAndrew Gallatin 
1181b2fc195eSAndrew Gallatin         sc = arg1;
1182b2fc195eSAndrew Gallatin         intr_coal_delay = sc->intr_coal_delay;
1183b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
1184b2fc195eSAndrew Gallatin         if (err != 0) {
1185b2fc195eSAndrew Gallatin                 return err;
1186b2fc195eSAndrew Gallatin         }
1187b2fc195eSAndrew Gallatin         if (intr_coal_delay == sc->intr_coal_delay)
1188b2fc195eSAndrew Gallatin                 return 0;
1189b2fc195eSAndrew Gallatin 
1190b2fc195eSAndrew Gallatin         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
1191b2fc195eSAndrew Gallatin                 return EINVAL;
1192b2fc195eSAndrew Gallatin 
1193a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
11945e7d8541SAndrew Gallatin 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
1195b2fc195eSAndrew Gallatin 	sc->intr_coal_delay = intr_coal_delay;
11965e7d8541SAndrew Gallatin 
1197a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1198b2fc195eSAndrew Gallatin         return err;
1199b2fc195eSAndrew Gallatin }
1200b2fc195eSAndrew Gallatin 
1201b2fc195eSAndrew Gallatin static int
12026d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
1203b2fc195eSAndrew Gallatin {
12046d87a65dSAndrew Gallatin         mxge_softc_t *sc;
1205b2fc195eSAndrew Gallatin         unsigned int enabled;
1206b2fc195eSAndrew Gallatin         int err;
1207b2fc195eSAndrew Gallatin 
1208b2fc195eSAndrew Gallatin         sc = arg1;
1209b2fc195eSAndrew Gallatin         enabled = sc->pause;
1210b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, &enabled, arg2, req);
1211b2fc195eSAndrew Gallatin         if (err != 0) {
1212b2fc195eSAndrew Gallatin                 return err;
1213b2fc195eSAndrew Gallatin         }
1214b2fc195eSAndrew Gallatin         if (enabled == sc->pause)
1215b2fc195eSAndrew Gallatin                 return 0;
1216b2fc195eSAndrew Gallatin 
1217a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
12186d87a65dSAndrew Gallatin 	err = mxge_change_pause(sc, enabled);
1219a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
1220b2fc195eSAndrew Gallatin         return err;
1221b2fc195eSAndrew Gallatin }
1222b2fc195eSAndrew Gallatin 
1223b2fc195eSAndrew Gallatin static int
12246d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS)
1225b2fc195eSAndrew Gallatin {
1226b2fc195eSAndrew Gallatin         int err;
1227b2fc195eSAndrew Gallatin 
1228b2fc195eSAndrew Gallatin         if (arg1 == NULL)
1229b2fc195eSAndrew Gallatin                 return EFAULT;
1230b2fc195eSAndrew Gallatin         arg2 = be32toh(*(int *)arg1);
1231b2fc195eSAndrew Gallatin         arg1 = NULL;
1232b2fc195eSAndrew Gallatin         err = sysctl_handle_int(oidp, arg1, arg2, req);
1233b2fc195eSAndrew Gallatin 
1234b2fc195eSAndrew Gallatin         return err;
1235b2fc195eSAndrew Gallatin }
1236b2fc195eSAndrew Gallatin 
1237b2fc195eSAndrew Gallatin static void
12386d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc)
1239b2fc195eSAndrew Gallatin {
1240b2fc195eSAndrew Gallatin 	struct sysctl_ctx_list *ctx;
1241b2fc195eSAndrew Gallatin 	struct sysctl_oid_list *children;
12425e7d8541SAndrew Gallatin 	mcp_irq_data_t *fw;
1243b2fc195eSAndrew Gallatin 
1244b2fc195eSAndrew Gallatin 	ctx = device_get_sysctl_ctx(sc->dev);
1245b2fc195eSAndrew Gallatin 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
1246b2fc195eSAndrew Gallatin 	fw = sc->fw_stats;
1247b2fc195eSAndrew Gallatin 
12485e7d8541SAndrew Gallatin 	/* random information */
12495e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12505e7d8541SAndrew Gallatin 		       "firmware_version",
12515e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->fw_version,
12525e7d8541SAndrew Gallatin 		       0, "firmware version");
12535e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12545e7d8541SAndrew Gallatin 		       "serial_number",
12555e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->serial_number_string,
12565e7d8541SAndrew Gallatin 		       0, "serial number");
12575e7d8541SAndrew Gallatin 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
12585e7d8541SAndrew Gallatin 		       "product_code",
12595e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->product_code_string,
12605e7d8541SAndrew Gallatin 		       0, "product_code");
12615e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1262d91b1b49SAndrew Gallatin 		       "pcie_link_width",
1263d91b1b49SAndrew Gallatin 		       CTLFLAG_RD, &sc->link_width,
1264d91b1b49SAndrew Gallatin 		       0, "tx_boundary");
1265d91b1b49SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12665e7d8541SAndrew Gallatin 		       "tx_boundary",
12675e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.boundary,
12685e7d8541SAndrew Gallatin 		       0, "tx_boundary");
12695e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1270091feecdSAndrew Gallatin 		       "write_combine",
1271091feecdSAndrew Gallatin 		       CTLFLAG_RD, &sc->wc,
1272091feecdSAndrew Gallatin 		       0, "write combining PIO?");
1273091feecdSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12745e7d8541SAndrew Gallatin 		       "read_dma_MBs",
12755e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_dma,
12765e7d8541SAndrew Gallatin 		       0, "DMA Read speed in MB/s");
12775e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12785e7d8541SAndrew Gallatin 		       "write_dma_MBs",
12795e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->write_dma,
12805e7d8541SAndrew Gallatin 		       0, "DMA Write speed in MB/s");
12815e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
12825e7d8541SAndrew Gallatin 		       "read_write_dma_MBs",
12835e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->read_write_dma,
12845e7d8541SAndrew Gallatin 		       0, "DMA concurrent Read/Write speed in MB/s");
12855e7d8541SAndrew Gallatin 
12865e7d8541SAndrew Gallatin 
12875e7d8541SAndrew Gallatin 	/* performance related tunables */
1288b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1289b2fc195eSAndrew Gallatin 			"intr_coal_delay",
1290b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
12916d87a65dSAndrew Gallatin 			0, mxge_change_intr_coal,
1292b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1293b2fc195eSAndrew Gallatin 
1294b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1295b2fc195eSAndrew Gallatin 			"flow_control_enabled",
1296b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RW, sc,
12976d87a65dSAndrew Gallatin 			0, mxge_change_flow_control,
1298b2fc195eSAndrew Gallatin 			"I", "interrupt coalescing delay in usecs");
1299b2fc195eSAndrew Gallatin 
1300b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13015e7d8541SAndrew Gallatin 		       "deassert_wait",
13025e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_deassert_wait,
13035e7d8541SAndrew Gallatin 		       0, "Wait for IRQ line to go low in ihandler");
1304b2fc195eSAndrew Gallatin 
1305b2fc195eSAndrew Gallatin 	/* stats block from firmware is in network byte order.
1306b2fc195eSAndrew Gallatin 	   Need to swap it */
1307b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1308b2fc195eSAndrew Gallatin 			"link_up",
1309b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
13106d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1311b2fc195eSAndrew Gallatin 			"I", "link up");
1312b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1313b2fc195eSAndrew Gallatin 			"rdma_tags_available",
1314b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
13156d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1316b2fc195eSAndrew Gallatin 			"I", "rdma_tags_available");
1317b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1318b2fc195eSAndrew Gallatin 			"dropped_link_overflow",
1319b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
13206d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1321b2fc195eSAndrew Gallatin 			"I", "dropped_link_overflow");
1322b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1323b2fc195eSAndrew Gallatin 			"dropped_link_error_or_filtered",
1324b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1325b2fc195eSAndrew Gallatin 			&fw->dropped_link_error_or_filtered,
13266d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1327b2fc195eSAndrew Gallatin 			"I", "dropped_link_error_or_filtered");
1328b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
13290fa7f681SAndrew Gallatin 			"dropped_multicast_filtered",
13300fa7f681SAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
13310fa7f681SAndrew Gallatin 			&fw->dropped_multicast_filtered,
13320fa7f681SAndrew Gallatin 			0, mxge_handle_be32,
13330fa7f681SAndrew Gallatin 			"I", "dropped_multicast_filtered");
13340fa7f681SAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1335b2fc195eSAndrew Gallatin 			"dropped_runt",
1336b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
13376d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1338b2fc195eSAndrew Gallatin 			"I", "dropped_runt");
1339b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1340b2fc195eSAndrew Gallatin 			"dropped_overrun",
1341b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
13426d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1343b2fc195eSAndrew Gallatin 			"I", "dropped_overrun");
1344b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1345b2fc195eSAndrew Gallatin 			"dropped_no_small_buffer",
1346b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD,
1347b2fc195eSAndrew Gallatin 			&fw->dropped_no_small_buffer,
13486d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1349b2fc195eSAndrew Gallatin 			"I", "dropped_no_small_buffer");
1350b2fc195eSAndrew Gallatin 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
1351b2fc195eSAndrew Gallatin 			"dropped_no_big_buffer",
1352b2fc195eSAndrew Gallatin 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
13536d87a65dSAndrew Gallatin 			0, mxge_handle_be32,
1354b2fc195eSAndrew Gallatin 			"I", "dropped_no_big_buffer");
1355b2fc195eSAndrew Gallatin 
1356b2fc195eSAndrew Gallatin 	/* host counters exported for debugging */
1357b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13585e7d8541SAndrew Gallatin 		       "rx_small_cnt",
13595e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_small.cnt,
13605e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
13615e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13625e7d8541SAndrew Gallatin 		       "rx_big_cnt",
13635e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->rx_big.cnt,
13645e7d8541SAndrew Gallatin 		       0, "rx_small_cnt");
13655e7d8541SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1366b2fc195eSAndrew Gallatin 		       "tx_req",
1367b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.req,
1368b2fc195eSAndrew Gallatin 		       0, "tx_req");
1369b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1370b2fc195eSAndrew Gallatin 		       "tx_done",
1371b2fc195eSAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.done,
1372b2fc195eSAndrew Gallatin 		       0, "tx_done");
1373b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13745e7d8541SAndrew Gallatin 		       "tx_pkt_done",
13755e7d8541SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.pkt_done,
13765e7d8541SAndrew Gallatin 		       0, "tx_done");
1377a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1378a82c2581SAndrew Gallatin 		       "tx_stall",
1379a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.stall,
1380a82c2581SAndrew Gallatin 		       0, "tx_stall");
1381a82c2581SAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1382a82c2581SAndrew Gallatin 		       "tx_wake",
1383a82c2581SAndrew Gallatin 		       CTLFLAG_RD, &sc->tx.wake,
1384a82c2581SAndrew Gallatin 		       0, "tx_wake");
13855e7d8541SAndrew Gallatin 
13865e7d8541SAndrew Gallatin 	/* verbose printing? */
1387b2fc195eSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
13885e7d8541SAndrew Gallatin 		       "verbose",
13895e7d8541SAndrew Gallatin 		       CTLFLAG_RW, &mxge_verbose,
13905e7d8541SAndrew Gallatin 		       0, "verbose printing");
1391b2fc195eSAndrew Gallatin 
1392053e637fSAndrew Gallatin 	/* lro */
1393053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1394053e637fSAndrew Gallatin 		       "lro_cnt", CTLFLAG_RW, &sc->lro_cnt,
1395053e637fSAndrew Gallatin 		       0, "number of lro merge queues");
1396053e637fSAndrew Gallatin 
1397053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1398053e637fSAndrew Gallatin 		       "lro_flushed", CTLFLAG_RD, &sc->lro_flushed,
1399053e637fSAndrew Gallatin 		       0, "number of lro merge queues flushed");
1400053e637fSAndrew Gallatin 
1401053e637fSAndrew Gallatin 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
1402053e637fSAndrew Gallatin 		       "lro_queued", CTLFLAG_RD, &sc->lro_queued,
1403053e637fSAndrew Gallatin 		       0, "number of frames appended to lro merge queues");
1404053e637fSAndrew Gallatin 
1405b2fc195eSAndrew Gallatin }
1406b2fc195eSAndrew Gallatin 
1407b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1408b2fc195eSAndrew Gallatin    backwards one at a time and handle ring wraps */
1409b2fc195eSAndrew Gallatin 
1410b2fc195eSAndrew Gallatin static inline void
14116d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx,
1412b2fc195eSAndrew Gallatin 			    mcp_kreq_ether_send_t *src, int cnt)
1413b2fc195eSAndrew Gallatin {
1414b2fc195eSAndrew Gallatin         int idx, starting_slot;
1415b2fc195eSAndrew Gallatin         starting_slot = tx->req;
1416b2fc195eSAndrew Gallatin         while (cnt > 1) {
1417b2fc195eSAndrew Gallatin                 cnt--;
1418b2fc195eSAndrew Gallatin                 idx = (starting_slot + cnt) & tx->mask;
14196d87a65dSAndrew Gallatin                 mxge_pio_copy(&tx->lanai[idx],
1420b2fc195eSAndrew Gallatin 			      &src[cnt], sizeof(*src));
1421b2fc195eSAndrew Gallatin                 mb();
1422b2fc195eSAndrew Gallatin         }
1423b2fc195eSAndrew Gallatin }
1424b2fc195eSAndrew Gallatin 
1425b2fc195eSAndrew Gallatin /*
1426b2fc195eSAndrew Gallatin  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
1427b2fc195eSAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
1428b2fc195eSAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's flags
1429b2fc195eSAndrew Gallatin  * to mark them valid only after writing the entire chain
1430b2fc195eSAndrew Gallatin  */
1431b2fc195eSAndrew Gallatin 
1432b2fc195eSAndrew Gallatin static inline void
14336d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src,
1434b2fc195eSAndrew Gallatin                   int cnt)
1435b2fc195eSAndrew Gallatin {
1436b2fc195eSAndrew Gallatin         int idx, i;
1437b2fc195eSAndrew Gallatin         uint32_t *src_ints;
1438b2fc195eSAndrew Gallatin 	volatile uint32_t *dst_ints;
1439b2fc195eSAndrew Gallatin         mcp_kreq_ether_send_t *srcp;
1440b2fc195eSAndrew Gallatin 	volatile mcp_kreq_ether_send_t *dstp, *dst;
14415e7d8541SAndrew Gallatin 	uint8_t last_flags;
1442b2fc195eSAndrew Gallatin 
1443b2fc195eSAndrew Gallatin         idx = tx->req & tx->mask;
1444b2fc195eSAndrew Gallatin 
14455e7d8541SAndrew Gallatin 	last_flags = src->flags;
14465e7d8541SAndrew Gallatin 	src->flags = 0;
1447b2fc195eSAndrew Gallatin         mb();
1448b2fc195eSAndrew Gallatin         dst = dstp = &tx->lanai[idx];
1449b2fc195eSAndrew Gallatin         srcp = src;
1450b2fc195eSAndrew Gallatin 
1451b2fc195eSAndrew Gallatin         if ((idx + cnt) < tx->mask) {
1452b2fc195eSAndrew Gallatin                 for (i = 0; i < (cnt - 1); i += 2) {
14536d87a65dSAndrew Gallatin                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
1454b2fc195eSAndrew Gallatin                         mb(); /* force write every 32 bytes */
1455b2fc195eSAndrew Gallatin                         srcp += 2;
1456b2fc195eSAndrew Gallatin                         dstp += 2;
1457b2fc195eSAndrew Gallatin                 }
1458b2fc195eSAndrew Gallatin         } else {
1459b2fc195eSAndrew Gallatin                 /* submit all but the first request, and ensure
1460b2fc195eSAndrew Gallatin                    that it is submitted below */
14616d87a65dSAndrew Gallatin                 mxge_submit_req_backwards(tx, src, cnt);
1462b2fc195eSAndrew Gallatin                 i = 0;
1463b2fc195eSAndrew Gallatin         }
1464b2fc195eSAndrew Gallatin         if (i < cnt) {
1465b2fc195eSAndrew Gallatin                 /* submit the first request */
14666d87a65dSAndrew Gallatin                 mxge_pio_copy(dstp, srcp, sizeof(*src));
1467b2fc195eSAndrew Gallatin                 mb(); /* barrier before setting valid flag */
1468b2fc195eSAndrew Gallatin         }
1469b2fc195eSAndrew Gallatin 
1470b2fc195eSAndrew Gallatin         /* re-write the last 32-bits with the valid flags */
14715e7d8541SAndrew Gallatin         src->flags = last_flags;
1472b2fc195eSAndrew Gallatin         src_ints = (uint32_t *)src;
1473b2fc195eSAndrew Gallatin         src_ints+=3;
1474b2fc195eSAndrew Gallatin         dst_ints = (volatile uint32_t *)dst;
1475b2fc195eSAndrew Gallatin         dst_ints+=3;
1476b2fc195eSAndrew Gallatin         *dst_ints =  *src_ints;
1477b2fc195eSAndrew Gallatin         tx->req += cnt;
1478b2fc195eSAndrew Gallatin         mb();
1479b2fc195eSAndrew Gallatin }
1480b2fc195eSAndrew Gallatin 
1481b2fc195eSAndrew Gallatin static inline void
14826d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt)
1483b2fc195eSAndrew Gallatin {
1484b2fc195eSAndrew Gallatin     tx->req += cnt;
1485b2fc195eSAndrew Gallatin     mb();
1486b2fc195eSAndrew Gallatin     while (cnt >= 4) {
14876d87a65dSAndrew Gallatin 	    mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64);
1488b2fc195eSAndrew Gallatin 	    mb();
1489b2fc195eSAndrew Gallatin 	    src += 4;
1490b2fc195eSAndrew Gallatin 	    cnt -= 4;
1491b2fc195eSAndrew Gallatin     }
1492b2fc195eSAndrew Gallatin     if (cnt > 0) {
1493b2fc195eSAndrew Gallatin 	    /* pad it to 64 bytes.  The src is 64 bytes bigger than it
1494b2fc195eSAndrew Gallatin 	       needs to be so that we don't overrun it */
14950fa7f681SAndrew Gallatin 	    mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64);
1496b2fc195eSAndrew Gallatin 	    mb();
1497b2fc195eSAndrew Gallatin     }
1498b2fc195eSAndrew Gallatin }
1499b2fc195eSAndrew Gallatin 
1500b2fc195eSAndrew Gallatin static void
1501aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt)
1502aed8e389SAndrew Gallatin {
1503aed8e389SAndrew Gallatin 	mxge_tx_buf_t *tx;
1504aed8e389SAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1505aed8e389SAndrew Gallatin 	bus_dma_segment_t *seg;
1506aed8e389SAndrew Gallatin 	struct ether_header *eh;
1507aed8e389SAndrew Gallatin 	struct ip *ip;
1508aed8e389SAndrew Gallatin 	struct tcphdr *tcp;
1509aed8e389SAndrew Gallatin 	uint32_t low, high_swapped;
1510aed8e389SAndrew Gallatin 	int len, seglen, cum_len, cum_len_next;
1511aed8e389SAndrew Gallatin 	int next_is_first, chop, cnt, rdma_count, small;
1512aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
1513aed8e389SAndrew Gallatin 	uint8_t flags, flags_next;
1514aed8e389SAndrew Gallatin 	static int once;
1515aed8e389SAndrew Gallatin 
1516aed8e389SAndrew Gallatin 	mss = m->m_pkthdr.tso_segsz;
1517aed8e389SAndrew Gallatin 
1518aed8e389SAndrew Gallatin 	/* negative cum_len signifies to the
1519aed8e389SAndrew Gallatin 	 * send loop that we are still in the
1520aed8e389SAndrew Gallatin 	 * header portion of the TSO packet.
1521aed8e389SAndrew Gallatin 	 */
1522aed8e389SAndrew Gallatin 
1523aed8e389SAndrew Gallatin 	/* ensure we have the ethernet, IP and TCP
1524aed8e389SAndrew Gallatin 	   header together in the first mbuf, copy
1525aed8e389SAndrew Gallatin 	   it to a scratch buffer if not */
1526aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh)
1527aed8e389SAndrew Gallatin 			    + sizeof (*ip))) {
1528aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1529aed8e389SAndrew Gallatin 			   sc->scratch);
1530aed8e389SAndrew Gallatin 		eh = (struct ether_header *)sc->scratch;
1531aed8e389SAndrew Gallatin 	} else {
1532aed8e389SAndrew Gallatin 		eh = mtod(m, struct ether_header *);
1533aed8e389SAndrew Gallatin 	}
1534aed8e389SAndrew Gallatin 	ip = (struct ip *) (eh + 1);
1535aed8e389SAndrew Gallatin 	if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2)
1536aed8e389SAndrew Gallatin 			    + sizeof (*tcp))) {
1537aed8e389SAndrew Gallatin 		m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2)
1538aed8e389SAndrew Gallatin 			   + sizeof (*tcp),  sc->scratch);
1539aed8e389SAndrew Gallatin 		eh = (struct ether_header *) sc->scratch;
1540aed8e389SAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1541aed8e389SAndrew Gallatin 	}
1542aed8e389SAndrew Gallatin 
1543aed8e389SAndrew Gallatin 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
1544aed8e389SAndrew Gallatin 	cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2));
1545aed8e389SAndrew Gallatin 
1546aed8e389SAndrew Gallatin 	/* TSO implies checksum offload on this hardware */
1547aed8e389SAndrew Gallatin 	cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1548aed8e389SAndrew Gallatin 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
1549aed8e389SAndrew Gallatin 
1550aed8e389SAndrew Gallatin 
1551aed8e389SAndrew Gallatin 	/* for TSO, pseudo_hdr_offset holds mss.
1552aed8e389SAndrew Gallatin 	 * The firmware figures out where to put
1553aed8e389SAndrew Gallatin 	 * the checksum by parsing the header. */
1554aed8e389SAndrew Gallatin 	pseudo_hdr_offset = htobe16(mss);
1555aed8e389SAndrew Gallatin 
1556aed8e389SAndrew Gallatin 	tx = &sc->tx;
1557aed8e389SAndrew Gallatin 	req = tx->req_list;
1558aed8e389SAndrew Gallatin 	seg = tx->seg_list;
1559aed8e389SAndrew Gallatin 	cnt = 0;
1560aed8e389SAndrew Gallatin 	rdma_count = 0;
1561aed8e389SAndrew Gallatin 	/* "rdma_count" is the number of RDMAs belonging to the
1562aed8e389SAndrew Gallatin 	 * current packet BEFORE the current send request. For
1563aed8e389SAndrew Gallatin 	 * non-TSO packets, this is equal to "count".
1564aed8e389SAndrew Gallatin 	 * For TSO packets, rdma_count needs to be reset
1565aed8e389SAndrew Gallatin 	 * to 0 after a segment cut.
1566aed8e389SAndrew Gallatin 	 *
1567aed8e389SAndrew Gallatin 	 * The rdma_count field of the send request is
1568aed8e389SAndrew Gallatin 	 * the number of RDMAs of the packet starting at
1569aed8e389SAndrew Gallatin 	 * that request. For TSO send requests with one ore more cuts
1570aed8e389SAndrew Gallatin 	 * in the middle, this is the number of RDMAs starting
1571aed8e389SAndrew Gallatin 	 * after the last cut in the request. All previous
1572aed8e389SAndrew Gallatin 	 * segments before the last cut implicitly have 1 RDMA.
1573aed8e389SAndrew Gallatin 	 *
1574aed8e389SAndrew Gallatin 	 * Since the number of RDMAs is not known beforehand,
1575aed8e389SAndrew Gallatin 	 * it must be filled-in retroactively - after each
1576aed8e389SAndrew Gallatin 	 * segmentation cut or at the end of the entire packet.
1577aed8e389SAndrew Gallatin 	 */
1578aed8e389SAndrew Gallatin 
1579aed8e389SAndrew Gallatin 	while (busdma_seg_cnt) {
1580aed8e389SAndrew Gallatin 		/* Break the busdma segment up into pieces*/
1581aed8e389SAndrew Gallatin 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
1582aed8e389SAndrew Gallatin 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1583e39a0a37SAndrew Gallatin 		len = seg->ds_len;
1584aed8e389SAndrew Gallatin 
1585aed8e389SAndrew Gallatin 		while (len) {
1586aed8e389SAndrew Gallatin 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
1587e39a0a37SAndrew Gallatin 			seglen = len;
1588aed8e389SAndrew Gallatin 			cum_len_next = cum_len + seglen;
1589aed8e389SAndrew Gallatin 			(req-rdma_count)->rdma_count = rdma_count + 1;
1590aed8e389SAndrew Gallatin 			if (__predict_true(cum_len >= 0)) {
1591aed8e389SAndrew Gallatin 				/* payload */
1592aed8e389SAndrew Gallatin 				chop = (cum_len_next > mss);
1593aed8e389SAndrew Gallatin 				cum_len_next = cum_len_next % mss;
1594aed8e389SAndrew Gallatin 				next_is_first = (cum_len_next == 0);
1595aed8e389SAndrew Gallatin 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1596aed8e389SAndrew Gallatin 				flags_next |= next_is_first *
1597aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST;
1598aed8e389SAndrew Gallatin 				rdma_count |= -(chop | next_is_first);
1599aed8e389SAndrew Gallatin 				rdma_count += chop & !next_is_first;
1600aed8e389SAndrew Gallatin 			} else if (cum_len_next >= 0) {
1601aed8e389SAndrew Gallatin 				/* header ends */
1602aed8e389SAndrew Gallatin 				rdma_count = -1;
1603aed8e389SAndrew Gallatin 				cum_len_next = 0;
1604aed8e389SAndrew Gallatin 				seglen = -cum_len;
1605aed8e389SAndrew Gallatin 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
1606aed8e389SAndrew Gallatin 				flags_next = MXGEFW_FLAGS_TSO_PLD |
1607aed8e389SAndrew Gallatin 					MXGEFW_FLAGS_FIRST |
1608aed8e389SAndrew Gallatin 					(small * MXGEFW_FLAGS_SMALL);
1609aed8e389SAndrew Gallatin 			    }
1610aed8e389SAndrew Gallatin 
1611aed8e389SAndrew Gallatin 			req->addr_high = high_swapped;
1612aed8e389SAndrew Gallatin 			req->addr_low = htobe32(low);
1613aed8e389SAndrew Gallatin 			req->pseudo_hdr_offset = pseudo_hdr_offset;
1614aed8e389SAndrew Gallatin 			req->pad = 0;
1615aed8e389SAndrew Gallatin 			req->rdma_count = 1;
1616aed8e389SAndrew Gallatin 			req->length = htobe16(seglen);
1617aed8e389SAndrew Gallatin 			req->cksum_offset = cksum_offset;
1618aed8e389SAndrew Gallatin 			req->flags = flags | ((cum_len & 1) *
1619aed8e389SAndrew Gallatin 					      MXGEFW_FLAGS_ALIGN_ODD);
1620aed8e389SAndrew Gallatin 			low += seglen;
1621aed8e389SAndrew Gallatin 			len -= seglen;
1622aed8e389SAndrew Gallatin 			cum_len = cum_len_next;
1623aed8e389SAndrew Gallatin 			flags = flags_next;
1624aed8e389SAndrew Gallatin 			req++;
1625aed8e389SAndrew Gallatin 			cnt++;
1626aed8e389SAndrew Gallatin 			rdma_count++;
1627aed8e389SAndrew Gallatin 			if (__predict_false(cksum_offset > seglen))
1628aed8e389SAndrew Gallatin 				cksum_offset -= seglen;
1629aed8e389SAndrew Gallatin 			else
1630aed8e389SAndrew Gallatin 				cksum_offset = 0;
1631aed8e389SAndrew Gallatin 			if (__predict_false(cnt > MXGE_MAX_SEND_DESC))
1632aed8e389SAndrew Gallatin 				goto drop;
1633aed8e389SAndrew Gallatin 		}
1634aed8e389SAndrew Gallatin 		busdma_seg_cnt--;
1635aed8e389SAndrew Gallatin 		seg++;
1636aed8e389SAndrew Gallatin 	}
1637aed8e389SAndrew Gallatin 	(req-rdma_count)->rdma_count = rdma_count;
1638aed8e389SAndrew Gallatin 
1639aed8e389SAndrew Gallatin 	do {
1640aed8e389SAndrew Gallatin 		req--;
1641aed8e389SAndrew Gallatin 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
1642aed8e389SAndrew Gallatin 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
1643aed8e389SAndrew Gallatin 
1644aed8e389SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1645aed8e389SAndrew Gallatin 	if (tx->wc_fifo == NULL)
1646aed8e389SAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1647aed8e389SAndrew Gallatin 	else
1648aed8e389SAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1649aed8e389SAndrew Gallatin 	return;
1650aed8e389SAndrew Gallatin 
1651aed8e389SAndrew Gallatin drop:
1652e39a0a37SAndrew Gallatin 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
1653aed8e389SAndrew Gallatin 	m_freem(m);
1654aed8e389SAndrew Gallatin 	sc->ifp->if_oerrors++;
1655aed8e389SAndrew Gallatin 	if (!once) {
1656aed8e389SAndrew Gallatin 		printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n");
1657aed8e389SAndrew Gallatin 		printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list);
1658aed8e389SAndrew Gallatin 		once = 1;
1659aed8e389SAndrew Gallatin 	}
1660aed8e389SAndrew Gallatin 	return;
1661aed8e389SAndrew Gallatin 
1662aed8e389SAndrew Gallatin }
1663aed8e389SAndrew Gallatin 
1664aed8e389SAndrew Gallatin static void
16656d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m)
1666b2fc195eSAndrew Gallatin {
1667b2fc195eSAndrew Gallatin 	mcp_kreq_ether_send_t *req;
1668b2fc195eSAndrew Gallatin 	bus_dma_segment_t *seg;
1669b2fc195eSAndrew Gallatin 	struct mbuf *m_tmp;
1670b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
16716d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
1672b2fc195eSAndrew Gallatin 	struct ether_header *eh;
1673b2fc195eSAndrew Gallatin 	struct ip *ip;
1674aed8e389SAndrew Gallatin 	int cnt, cum_len, err, i, idx, odd_flag;
1675aed8e389SAndrew Gallatin 	uint16_t pseudo_hdr_offset;
1676aed8e389SAndrew Gallatin         uint8_t flags, cksum_offset;
1677b2fc195eSAndrew Gallatin 
1678b2fc195eSAndrew Gallatin 
1679b2fc195eSAndrew Gallatin 
1680b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
1681b2fc195eSAndrew Gallatin 	tx = &sc->tx;
1682b2fc195eSAndrew Gallatin 
1683b2fc195eSAndrew Gallatin 	/* (try to) map the frame for DMA */
1684b2fc195eSAndrew Gallatin 	idx = tx->req & tx->mask;
1685b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map,
1686aed8e389SAndrew Gallatin 				      m, tx->seg_list, &cnt,
1687b2fc195eSAndrew Gallatin 				      BUS_DMA_NOWAIT);
1688b2fc195eSAndrew Gallatin 	if (err == EFBIG) {
1689b2fc195eSAndrew Gallatin 		/* Too many segments in the chain.  Try
1690b2fc195eSAndrew Gallatin 		   to defrag */
1691b2fc195eSAndrew Gallatin 		m_tmp = m_defrag(m, M_NOWAIT);
1692b2fc195eSAndrew Gallatin 		if (m_tmp == NULL) {
1693b2fc195eSAndrew Gallatin 			goto drop;
1694b2fc195eSAndrew Gallatin 		}
1695b2fc195eSAndrew Gallatin 		m = m_tmp;
1696b2fc195eSAndrew Gallatin 		err = bus_dmamap_load_mbuf_sg(tx->dmat,
1697b2fc195eSAndrew Gallatin 					      tx->info[idx].map,
1698aed8e389SAndrew Gallatin 					      m, tx->seg_list, &cnt,
1699b2fc195eSAndrew Gallatin 					      BUS_DMA_NOWAIT);
1700b2fc195eSAndrew Gallatin 	}
1701b2fc195eSAndrew Gallatin 	if (err != 0) {
1702aed8e389SAndrew Gallatin 		device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d"
1703aed8e389SAndrew Gallatin 			      " packet len = %d\n", err, m->m_pkthdr.len);
1704b2fc195eSAndrew Gallatin 		goto drop;
1705b2fc195eSAndrew Gallatin 	}
1706b2fc195eSAndrew Gallatin 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
1707b2fc195eSAndrew Gallatin 			BUS_DMASYNC_PREWRITE);
17085e7d8541SAndrew Gallatin 	tx->info[idx].m = m;
1709b2fc195eSAndrew Gallatin 
1710aed8e389SAndrew Gallatin 
1711aed8e389SAndrew Gallatin 	/* TSO is different enough, we handle it in another routine */
1712aed8e389SAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
1713aed8e389SAndrew Gallatin 		mxge_encap_tso(sc, m, cnt);
1714aed8e389SAndrew Gallatin 		return;
1715aed8e389SAndrew Gallatin 	}
1716aed8e389SAndrew Gallatin 
1717b2fc195eSAndrew Gallatin 	req = tx->req_list;
1718b2fc195eSAndrew Gallatin 	cksum_offset = 0;
17195e7d8541SAndrew Gallatin 	pseudo_hdr_offset = 0;
17205e7d8541SAndrew Gallatin 	flags = MXGEFW_FLAGS_NO_TSO;
1721b2fc195eSAndrew Gallatin 
1722b2fc195eSAndrew Gallatin 	/* checksum offloading? */
1723b2fc195eSAndrew Gallatin 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
1724aed8e389SAndrew Gallatin 		/* ensure ip header is in first mbuf, copy
1725aed8e389SAndrew Gallatin 		   it to a scratch buffer if not */
1726aed8e389SAndrew Gallatin 		if (__predict_false(m->m_len < sizeof (*eh)
1727aed8e389SAndrew Gallatin 				    + sizeof (*ip))) {
1728aed8e389SAndrew Gallatin 			m_copydata(m, 0, sizeof (*eh) + sizeof (*ip),
1729aed8e389SAndrew Gallatin 				   sc->scratch);
1730aed8e389SAndrew Gallatin 			eh = (struct ether_header *)sc->scratch;
1731aed8e389SAndrew Gallatin 		} else {
1732b2fc195eSAndrew Gallatin 			eh = mtod(m, struct ether_header *);
1733aed8e389SAndrew Gallatin 		}
1734b2fc195eSAndrew Gallatin 		ip = (struct ip *) (eh + 1);
1735b2fc195eSAndrew Gallatin 		cksum_offset = sizeof(*eh) + (ip->ip_hl << 2);
1736b2fc195eSAndrew Gallatin 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
17375e7d8541SAndrew Gallatin 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
1738b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
17395e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_CKSUM;
1740aed8e389SAndrew Gallatin 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
1741aed8e389SAndrew Gallatin 	} else {
1742aed8e389SAndrew Gallatin 		odd_flag = 0;
1743b2fc195eSAndrew Gallatin 	}
17445e7d8541SAndrew Gallatin 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
17455e7d8541SAndrew Gallatin 		flags |= MXGEFW_FLAGS_SMALL;
1746b2fc195eSAndrew Gallatin 
1747b2fc195eSAndrew Gallatin 	/* convert segments into a request list */
1748b2fc195eSAndrew Gallatin 	cum_len = 0;
1749aed8e389SAndrew Gallatin 	seg = tx->seg_list;
17505e7d8541SAndrew Gallatin 	req->flags = MXGEFW_FLAGS_FIRST;
1751b2fc195eSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1752b2fc195eSAndrew Gallatin 		req->addr_low =
17536d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1754b2fc195eSAndrew Gallatin 		req->addr_high =
17556d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
1756b2fc195eSAndrew Gallatin 		req->length = htobe16(seg->ds_len);
1757b2fc195eSAndrew Gallatin 		req->cksum_offset = cksum_offset;
1758b2fc195eSAndrew Gallatin 		if (cksum_offset > seg->ds_len)
1759b2fc195eSAndrew Gallatin 			cksum_offset -= seg->ds_len;
1760b2fc195eSAndrew Gallatin 		else
1761b2fc195eSAndrew Gallatin 			cksum_offset = 0;
17625e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
17635e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
17645e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1765aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1766b2fc195eSAndrew Gallatin 		cum_len += seg->ds_len;
1767b2fc195eSAndrew Gallatin 		seg++;
1768b2fc195eSAndrew Gallatin 		req++;
1769b2fc195eSAndrew Gallatin 		req->flags = 0;
1770b2fc195eSAndrew Gallatin 	}
1771b2fc195eSAndrew Gallatin 	req--;
1772b2fc195eSAndrew Gallatin 	/* pad runts to 60 bytes */
1773b2fc195eSAndrew Gallatin 	if (cum_len < 60) {
1774b2fc195eSAndrew Gallatin 		req++;
1775b2fc195eSAndrew Gallatin 		req->addr_low =
17766d87a65dSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
1777b2fc195eSAndrew Gallatin 		req->addr_high =
17786d87a65dSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
1779b2fc195eSAndrew Gallatin 		req->length = htobe16(60 - cum_len);
17805e7d8541SAndrew Gallatin 		req->cksum_offset = 0;
17815e7d8541SAndrew Gallatin 		req->pseudo_hdr_offset = pseudo_hdr_offset;
17825e7d8541SAndrew Gallatin 		req->pad = 0; /* complete solid 16-byte block */
17835e7d8541SAndrew Gallatin 		req->rdma_count = 1;
1784aed8e389SAndrew Gallatin 		req->flags |= flags | ((cum_len & 1) * odd_flag);
1785b2fc195eSAndrew Gallatin 		cnt++;
1786b2fc195eSAndrew Gallatin 	}
17875e7d8541SAndrew Gallatin 
17885e7d8541SAndrew Gallatin 	tx->req_list[0].rdma_count = cnt;
17895e7d8541SAndrew Gallatin #if 0
17905e7d8541SAndrew Gallatin 	/* print what the firmware will see */
17915e7d8541SAndrew Gallatin 	for (i = 0; i < cnt; i++) {
17925e7d8541SAndrew Gallatin 		printf("%d: addr: 0x%x 0x%x len:%d pso%d,"
17935e7d8541SAndrew Gallatin 		    "cso:%d, flags:0x%x, rdma:%d\n",
17945e7d8541SAndrew Gallatin 		    i, (int)ntohl(tx->req_list[i].addr_high),
17955e7d8541SAndrew Gallatin 		    (int)ntohl(tx->req_list[i].addr_low),
17965e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].length),
17975e7d8541SAndrew Gallatin 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
17985e7d8541SAndrew Gallatin 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
17995e7d8541SAndrew Gallatin 		    tx->req_list[i].rdma_count);
18005e7d8541SAndrew Gallatin 	}
18015e7d8541SAndrew Gallatin 	printf("--------------\n");
18025e7d8541SAndrew Gallatin #endif
18035e7d8541SAndrew Gallatin 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
1804b2fc195eSAndrew Gallatin 	if (tx->wc_fifo == NULL)
18056d87a65dSAndrew Gallatin 		mxge_submit_req(tx, tx->req_list, cnt);
1806b2fc195eSAndrew Gallatin 	else
18076d87a65dSAndrew Gallatin 		mxge_submit_req_wc(tx, tx->req_list, cnt);
1808b2fc195eSAndrew Gallatin 	return;
1809b2fc195eSAndrew Gallatin 
1810b2fc195eSAndrew Gallatin drop:
1811b2fc195eSAndrew Gallatin 	m_freem(m);
1812b2fc195eSAndrew Gallatin 	ifp->if_oerrors++;
1813b2fc195eSAndrew Gallatin 	return;
1814b2fc195eSAndrew Gallatin }
1815b2fc195eSAndrew Gallatin 
1816b2fc195eSAndrew Gallatin 
18176d914a32SAndrew Gallatin 
18186d914a32SAndrew Gallatin 
18196d914a32SAndrew Gallatin static inline void
18206d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc)
1821b2fc195eSAndrew Gallatin {
1822b2fc195eSAndrew Gallatin 	struct mbuf *m;
1823b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
1824b2fc195eSAndrew Gallatin 
1825b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
18266d914a32SAndrew Gallatin 	while ((sc->tx.mask - (sc->tx.req - sc->tx.done))
18276d914a32SAndrew Gallatin 	       > MXGE_MAX_SEND_DESC) {
1828b2fc195eSAndrew Gallatin 
18296d914a32SAndrew Gallatin 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
18306d914a32SAndrew Gallatin 		if (m == NULL) {
18316d914a32SAndrew Gallatin 			return;
18326d914a32SAndrew Gallatin 		}
1833b2fc195eSAndrew Gallatin 		/* let BPF see it */
1834b2fc195eSAndrew Gallatin 		BPF_MTAP(ifp, m);
1835b2fc195eSAndrew Gallatin 
1836b2fc195eSAndrew Gallatin 		/* give it to the nic */
18376d87a65dSAndrew Gallatin 		mxge_encap(sc, m);
18386d914a32SAndrew Gallatin 	}
18396d914a32SAndrew Gallatin 	/* ran out of transmit slots */
1840a82c2581SAndrew Gallatin 	if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
1841b2fc195eSAndrew Gallatin 		sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1842a82c2581SAndrew Gallatin 		sc->tx.stall++;
1843a82c2581SAndrew Gallatin 	}
1844b2fc195eSAndrew Gallatin }
1845b2fc195eSAndrew Gallatin 
1846b2fc195eSAndrew Gallatin static void
18476d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp)
1848b2fc195eSAndrew Gallatin {
18496d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
1850b2fc195eSAndrew Gallatin 
1851b2fc195eSAndrew Gallatin 
1852a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->tx_mtx);
18536d87a65dSAndrew Gallatin 	mxge_start_locked(sc);
1854a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->tx_mtx);
1855b2fc195eSAndrew Gallatin }
1856b2fc195eSAndrew Gallatin 
18575e7d8541SAndrew Gallatin /*
18585e7d8541SAndrew Gallatin  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
18595e7d8541SAndrew Gallatin  * at most 32 bytes at a time, so as to avoid involving the software
18605e7d8541SAndrew Gallatin  * pio handler in the nic.   We re-write the first segment's low
18615e7d8541SAndrew Gallatin  * DMA address to mark it valid only after we write the entire chunk
18625e7d8541SAndrew Gallatin  * in a burst
18635e7d8541SAndrew Gallatin  */
18645e7d8541SAndrew Gallatin static inline void
18655e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
18665e7d8541SAndrew Gallatin 		mcp_kreq_ether_recv_t *src)
18675e7d8541SAndrew Gallatin {
18685e7d8541SAndrew Gallatin 	uint32_t low;
18695e7d8541SAndrew Gallatin 
18705e7d8541SAndrew Gallatin 	low = src->addr_low;
18715e7d8541SAndrew Gallatin 	src->addr_low = 0xffffffff;
1872a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
1873a1480dfbSAndrew Gallatin 	mb();
1874a1480dfbSAndrew Gallatin 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
18755e7d8541SAndrew Gallatin 	mb();
187640385a5fSAndrew Gallatin 	src->addr_low = low;
18775e7d8541SAndrew Gallatin 	dst->addr_low = low;
18785e7d8541SAndrew Gallatin 	mb();
18795e7d8541SAndrew Gallatin }
18805e7d8541SAndrew Gallatin 
1881b2fc195eSAndrew Gallatin static int
18826d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1883b2fc195eSAndrew Gallatin {
1884b2fc195eSAndrew Gallatin 	bus_dma_segment_t seg;
1885b2fc195eSAndrew Gallatin 	struct mbuf *m;
18866d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_small;
1887b2fc195eSAndrew Gallatin 	int cnt, err;
1888b2fc195eSAndrew Gallatin 
1889b2fc195eSAndrew Gallatin 	m = m_gethdr(M_DONTWAIT, MT_DATA);
1890b2fc195eSAndrew Gallatin 	if (m == NULL) {
1891b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1892b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1893b2fc195eSAndrew Gallatin 		goto done;
1894b2fc195eSAndrew Gallatin 	}
1895b2fc195eSAndrew Gallatin 	m->m_len = MHLEN;
1896b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1897b2fc195eSAndrew Gallatin 				      &seg, &cnt, BUS_DMA_NOWAIT);
1898b2fc195eSAndrew Gallatin 	if (err != 0) {
1899b2fc195eSAndrew Gallatin 		m_free(m);
1900b2fc195eSAndrew Gallatin 		goto done;
1901b2fc195eSAndrew Gallatin 	}
1902b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1903b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_low =
19046d87a65dSAndrew Gallatin 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
1905b2fc195eSAndrew Gallatin 	rx->shadow[idx].addr_high =
19066d87a65dSAndrew Gallatin 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
1907b2fc195eSAndrew Gallatin 
1908b2fc195eSAndrew Gallatin done:
1909b2fc195eSAndrew Gallatin 	if ((idx & 7) == 7) {
19105e7d8541SAndrew Gallatin 		if (rx->wc_fifo == NULL)
19115e7d8541SAndrew Gallatin 			mxge_submit_8rx(&rx->lanai[idx - 7],
19125e7d8541SAndrew Gallatin 					&rx->shadow[idx - 7]);
19135e7d8541SAndrew Gallatin 		else {
1914b2fc195eSAndrew Gallatin 			mb();
19155e7d8541SAndrew Gallatin 			mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
19165e7d8541SAndrew Gallatin 		}
1917b2fc195eSAndrew Gallatin         }
1918b2fc195eSAndrew Gallatin 	return err;
1919b2fc195eSAndrew Gallatin }
1920b2fc195eSAndrew Gallatin 
1921b2fc195eSAndrew Gallatin static int
19226d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx)
1923b2fc195eSAndrew Gallatin {
1924053e637fSAndrew Gallatin 	bus_dma_segment_t seg[3];
1925b2fc195eSAndrew Gallatin 	struct mbuf *m;
19266d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx = &sc->rx_big;
1927053e637fSAndrew Gallatin 	int cnt, err, i;
1928b2fc195eSAndrew Gallatin 
1929053e637fSAndrew Gallatin 	m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
1930b2fc195eSAndrew Gallatin 	if (m == NULL) {
1931b2fc195eSAndrew Gallatin 		rx->alloc_fail++;
1932b2fc195eSAndrew Gallatin 		err = ENOBUFS;
1933b2fc195eSAndrew Gallatin 		goto done;
1934b2fc195eSAndrew Gallatin 	}
1935053e637fSAndrew Gallatin 	m->m_len = rx->cl_size;
1936b2fc195eSAndrew Gallatin 	err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m,
1937053e637fSAndrew Gallatin 				      seg, &cnt, BUS_DMA_NOWAIT);
1938b2fc195eSAndrew Gallatin 	if (err != 0) {
1939b2fc195eSAndrew Gallatin 		m_free(m);
1940b2fc195eSAndrew Gallatin 		goto done;
1941b2fc195eSAndrew Gallatin 	}
1942b2fc195eSAndrew Gallatin 	rx->info[idx].m = m;
1943053e637fSAndrew Gallatin 
1944053e637fSAndrew Gallatin 	for (i = 0; i < cnt; i++) {
1945053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_low =
1946053e637fSAndrew Gallatin 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
1947053e637fSAndrew Gallatin 		rx->shadow[idx + i].addr_high =
1948053e637fSAndrew Gallatin 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
1949053e637fSAndrew Gallatin        }
1950053e637fSAndrew Gallatin 
1951b2fc195eSAndrew Gallatin 
1952b2fc195eSAndrew Gallatin done:
1953053e637fSAndrew Gallatin        for (i = 0; i < rx->nbufs; i++) {
1954b2fc195eSAndrew Gallatin 		if ((idx & 7) == 7) {
19555e7d8541SAndrew Gallatin 			if (rx->wc_fifo == NULL)
19565e7d8541SAndrew Gallatin 				mxge_submit_8rx(&rx->lanai[idx - 7],
19575e7d8541SAndrew Gallatin 						&rx->shadow[idx - 7]);
19585e7d8541SAndrew Gallatin 			else {
1959b2fc195eSAndrew Gallatin 				mb();
19605e7d8541SAndrew Gallatin 				mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64);
19615e7d8541SAndrew Gallatin 			}
1962b2fc195eSAndrew Gallatin 		}
1963053e637fSAndrew Gallatin 		idx++;
1964053e637fSAndrew Gallatin 	}
1965b2fc195eSAndrew Gallatin 	return err;
1966b2fc195eSAndrew Gallatin }
1967b2fc195eSAndrew Gallatin 
19689b03b0f3SAndrew Gallatin /*
19699b03b0f3SAndrew Gallatin  *  Myri10GE hardware checksums are not valid if the sender
19709b03b0f3SAndrew Gallatin  *  padded the frame with non-zero padding.  This is because
19719b03b0f3SAndrew Gallatin  *  the firmware just does a simple 16-bit 1s complement
19729b03b0f3SAndrew Gallatin  *  checksum across the entire frame, excluding the first 14
1973053e637fSAndrew Gallatin  *  bytes.  It is best to simply to check the checksum and
1974053e637fSAndrew Gallatin  *  tell the stack about it only if the checksum is good
19759b03b0f3SAndrew Gallatin  */
19769b03b0f3SAndrew Gallatin 
1977053e637fSAndrew Gallatin static inline uint16_t
1978053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum)
1979053e637fSAndrew Gallatin {
1980053e637fSAndrew Gallatin 	struct ether_header *eh;
1981053e637fSAndrew Gallatin 	struct ip *ip;
1982053e637fSAndrew Gallatin 	uint16_t c;
1983053e637fSAndrew Gallatin 
1984053e637fSAndrew Gallatin 	eh = mtod(m, struct ether_header *);
1985053e637fSAndrew Gallatin 
1986053e637fSAndrew Gallatin 	/* only deal with IPv4 TCP & UDP for now */
1987053e637fSAndrew Gallatin 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
1988053e637fSAndrew Gallatin 		return 1;
1989053e637fSAndrew Gallatin 	ip = (struct ip *)(eh + 1);
1990053e637fSAndrew Gallatin 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
1991053e637fSAndrew Gallatin 			    ip->ip_p != IPPROTO_UDP))
1992053e637fSAndrew Gallatin 		return 1;
1993053e637fSAndrew Gallatin 
1994053e637fSAndrew Gallatin 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
1995053e637fSAndrew Gallatin 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
1996053e637fSAndrew Gallatin 			    - (ip->ip_hl << 2) + ip->ip_p));
1997053e637fSAndrew Gallatin 	c ^= 0xffff;
1998053e637fSAndrew Gallatin 	return (c);
19995e7d8541SAndrew Gallatin }
2000053e637fSAndrew Gallatin 
20015e7d8541SAndrew Gallatin 
20025e7d8541SAndrew Gallatin static inline void
2003053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2004b2fc195eSAndrew Gallatin {
2005b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2006053e637fSAndrew Gallatin 	struct mbuf *m;
20076d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2008053e637fSAndrew Gallatin 	bus_dmamap_t old_map;
2009b2fc195eSAndrew Gallatin 	int idx;
2010053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2011b2fc195eSAndrew Gallatin 
2012b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2013053e637fSAndrew Gallatin 	rx = &sc->rx_big;
2014b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2015053e637fSAndrew Gallatin 	rx->cnt += rx->nbufs;
2016b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2017b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2018b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
20196d87a65dSAndrew Gallatin 	if (mxge_get_buf_big(sc, rx->extra_map, idx)) {
2020053e637fSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2021053e637fSAndrew Gallatin 		ifp->if_ierrors++;
2022053e637fSAndrew Gallatin 		return;
2023b2fc195eSAndrew Gallatin 	}
2024053e637fSAndrew Gallatin 
2025b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2026b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2027b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2028b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2029b2fc195eSAndrew Gallatin 
2030b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2031b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2032b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2033b2fc195eSAndrew Gallatin 
2034053e637fSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2035053e637fSAndrew Gallatin 	 * aligned */
20365e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2037b2fc195eSAndrew Gallatin 
2038053e637fSAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
2039053e637fSAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
20409b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2041b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2042053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2043053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2044b2fc195eSAndrew Gallatin 			return;
2045053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2046053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2047053e637fSAndrew Gallatin 		   checksum is good */
2048053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2049053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2050b2fc195eSAndrew Gallatin 	}
2051053e637fSAndrew Gallatin 	/* pass the frame up the stack */
2052053e637fSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2053b2fc195eSAndrew Gallatin }
2054b2fc195eSAndrew Gallatin 
2055b2fc195eSAndrew Gallatin static inline void
20565e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum)
2057b2fc195eSAndrew Gallatin {
2058b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
2059b2fc195eSAndrew Gallatin 	struct mbuf *m;
20606d87a65dSAndrew Gallatin 	mxge_rx_buf_t *rx;
2061b2fc195eSAndrew Gallatin 	bus_dmamap_t old_map;
2062b2fc195eSAndrew Gallatin 	int idx;
2063053e637fSAndrew Gallatin 	uint16_t tcpudp_csum;
2064b2fc195eSAndrew Gallatin 
2065b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
2066b2fc195eSAndrew Gallatin 	rx = &sc->rx_small;
2067b2fc195eSAndrew Gallatin 	idx = rx->cnt & rx->mask;
2068b2fc195eSAndrew Gallatin 	rx->cnt++;
2069b2fc195eSAndrew Gallatin 	/* save a pointer to the received mbuf */
2070b2fc195eSAndrew Gallatin 	m = rx->info[idx].m;
2071b2fc195eSAndrew Gallatin 	/* try to replace the received mbuf */
20726d87a65dSAndrew Gallatin 	if (mxge_get_buf_small(sc, rx->extra_map, idx)) {
2073b2fc195eSAndrew Gallatin 		/* drop the frame -- the old mbuf is re-cycled */
2074b2fc195eSAndrew Gallatin 		ifp->if_ierrors++;
2075b2fc195eSAndrew Gallatin 		return;
2076b2fc195eSAndrew Gallatin 	}
2077b2fc195eSAndrew Gallatin 
2078b2fc195eSAndrew Gallatin 	/* unmap the received buffer */
2079b2fc195eSAndrew Gallatin 	old_map = rx->info[idx].map;
2080b2fc195eSAndrew Gallatin 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
2081b2fc195eSAndrew Gallatin 	bus_dmamap_unload(rx->dmat, old_map);
2082b2fc195eSAndrew Gallatin 
2083b2fc195eSAndrew Gallatin 	/* swap the bus_dmamap_t's */
2084b2fc195eSAndrew Gallatin 	rx->info[idx].map = rx->extra_map;
2085b2fc195eSAndrew Gallatin 	rx->extra_map = old_map;
2086b2fc195eSAndrew Gallatin 
2087b2fc195eSAndrew Gallatin 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
2088b2fc195eSAndrew Gallatin 	 * aligned */
20895e7d8541SAndrew Gallatin 	m->m_data += MXGEFW_PAD;
2090b2fc195eSAndrew Gallatin 
20919b03b0f3SAndrew Gallatin 	m->m_pkthdr.rcvif = ifp;
20929b03b0f3SAndrew Gallatin 	m->m_len = m->m_pkthdr.len = len;
20939b03b0f3SAndrew Gallatin 	ifp->if_ipackets++;
2094b2fc195eSAndrew Gallatin 	/* if the checksum is valid, mark it in the mbuf header */
2095053e637fSAndrew Gallatin 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
2096053e637fSAndrew Gallatin 		if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum)))
2097053e637fSAndrew Gallatin 			return;
2098053e637fSAndrew Gallatin 		/* otherwise, it was a UDP frame, or a TCP frame which
2099053e637fSAndrew Gallatin 		   we could not do LRO on.  Tell the stack that the
2100053e637fSAndrew Gallatin 		   checksum is good */
2101053e637fSAndrew Gallatin 		m->m_pkthdr.csum_data = 0xffff;
2102053e637fSAndrew Gallatin 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
2103053e637fSAndrew Gallatin 	}
2104b2fc195eSAndrew Gallatin 
2105b2fc195eSAndrew Gallatin 	/* pass the frame up the stack */
2106b2fc195eSAndrew Gallatin 	(*ifp->if_input)(ifp, m);
2107b2fc195eSAndrew Gallatin }
2108b2fc195eSAndrew Gallatin 
2109b2fc195eSAndrew Gallatin static inline void
21105e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc)
21115e7d8541SAndrew Gallatin {
21125e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
2113053e637fSAndrew Gallatin 	struct lro_entry *lro;
21145e7d8541SAndrew Gallatin 	int limit = 0;
21155e7d8541SAndrew Gallatin 	uint16_t length;
21165e7d8541SAndrew Gallatin 	uint16_t checksum;
21175e7d8541SAndrew Gallatin 
21185e7d8541SAndrew Gallatin 
21195e7d8541SAndrew Gallatin 	while (rx_done->entry[rx_done->idx].length != 0) {
21205e7d8541SAndrew Gallatin 		length = ntohs(rx_done->entry[rx_done->idx].length);
21215e7d8541SAndrew Gallatin 		rx_done->entry[rx_done->idx].length = 0;
2122053e637fSAndrew Gallatin 		checksum = rx_done->entry[rx_done->idx].checksum;
2123b4db9009SAndrew Gallatin 		if (length <= (MHLEN - MXGEFW_PAD))
21245e7d8541SAndrew Gallatin 			mxge_rx_done_small(sc, length, checksum);
21255e7d8541SAndrew Gallatin 		else
21265e7d8541SAndrew Gallatin 			mxge_rx_done_big(sc, length, checksum);
21275e7d8541SAndrew Gallatin 		rx_done->cnt++;
21285e7d8541SAndrew Gallatin 		rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1);
21295e7d8541SAndrew Gallatin 
21305e7d8541SAndrew Gallatin 		/* limit potential for livelock */
21315e7d8541SAndrew Gallatin 		if (__predict_false(++limit > 2 * mxge_max_intr_slots))
21325e7d8541SAndrew Gallatin 			break;
2133053e637fSAndrew Gallatin 	}
2134053e637fSAndrew Gallatin 	while(!SLIST_EMPTY(&sc->lro_active)) {
2135053e637fSAndrew Gallatin 		lro = SLIST_FIRST(&sc->lro_active);
2136053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_active, next);
2137053e637fSAndrew Gallatin 		mxge_lro_flush(sc, lro);
21385e7d8541SAndrew Gallatin 	}
21395e7d8541SAndrew Gallatin }
21405e7d8541SAndrew Gallatin 
21415e7d8541SAndrew Gallatin 
21425e7d8541SAndrew Gallatin static inline void
21436d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx)
2144b2fc195eSAndrew Gallatin {
2145b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
21466d87a65dSAndrew Gallatin 	mxge_tx_buf_t *tx;
2147b2fc195eSAndrew Gallatin 	struct mbuf *m;
2148b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
21495e7d8541SAndrew Gallatin 	int idx, limit;
2150b2fc195eSAndrew Gallatin 
21515e7d8541SAndrew Gallatin 	limit = 0;
2152b2fc195eSAndrew Gallatin 	tx = &sc->tx;
2153b2fc195eSAndrew Gallatin 	ifp = sc->ifp;
21545e7d8541SAndrew Gallatin 	while (tx->pkt_done != mcp_idx) {
2155b2fc195eSAndrew Gallatin 		idx = tx->done & tx->mask;
2156b2fc195eSAndrew Gallatin 		tx->done++;
2157b2fc195eSAndrew Gallatin 		m = tx->info[idx].m;
2158b2fc195eSAndrew Gallatin 		/* mbuf and DMA map only attached to the first
2159b2fc195eSAndrew Gallatin 		   segment per-mbuf */
2160b2fc195eSAndrew Gallatin 		if (m != NULL) {
2161b2fc195eSAndrew Gallatin 			ifp->if_opackets++;
2162b2fc195eSAndrew Gallatin 			tx->info[idx].m = NULL;
2163b2fc195eSAndrew Gallatin 			map = tx->info[idx].map;
2164b2fc195eSAndrew Gallatin 			bus_dmamap_unload(tx->dmat, map);
2165b2fc195eSAndrew Gallatin 			m_freem(m);
2166b2fc195eSAndrew Gallatin 		}
21675e7d8541SAndrew Gallatin 		if (tx->info[idx].flag) {
21685e7d8541SAndrew Gallatin 			tx->info[idx].flag = 0;
21695e7d8541SAndrew Gallatin 			tx->pkt_done++;
21705e7d8541SAndrew Gallatin 		}
21715e7d8541SAndrew Gallatin 		/* limit potential for livelock by only handling
21725e7d8541SAndrew Gallatin 		   2 full tx rings per call */
21735e7d8541SAndrew Gallatin 		if (__predict_false(++limit >  2 * tx->mask))
21745e7d8541SAndrew Gallatin 			break;
2175b2fc195eSAndrew Gallatin 	}
2176b2fc195eSAndrew Gallatin 
2177b2fc195eSAndrew Gallatin 	/* If we have space, clear IFF_OACTIVE to tell the stack that
2178b2fc195eSAndrew Gallatin            its OK to send packets */
2179b2fc195eSAndrew Gallatin 
2180b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_OACTIVE &&
2181b2fc195eSAndrew Gallatin 	    tx->req - tx->done < (tx->mask + 1)/4) {
2182a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->tx_mtx);
2183b2fc195eSAndrew Gallatin 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2184a82c2581SAndrew Gallatin 		sc->tx.wake++;
21856d87a65dSAndrew Gallatin 		mxge_start_locked(sc);
2186a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->tx_mtx);
2187b2fc195eSAndrew Gallatin 	}
2188b2fc195eSAndrew Gallatin }
2189b2fc195eSAndrew Gallatin 
2190b2fc195eSAndrew Gallatin static void
21916d87a65dSAndrew Gallatin mxge_intr(void *arg)
2192b2fc195eSAndrew Gallatin {
21936d87a65dSAndrew Gallatin 	mxge_softc_t *sc = arg;
21945e7d8541SAndrew Gallatin 	mcp_irq_data_t *stats = sc->fw_stats;
21955e7d8541SAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
21965e7d8541SAndrew Gallatin 	mxge_rx_done_t *rx_done = &sc->rx_done;
21975e7d8541SAndrew Gallatin 	uint32_t send_done_count;
21985e7d8541SAndrew Gallatin 	uint8_t valid;
2199b2fc195eSAndrew Gallatin 
2200b2fc195eSAndrew Gallatin 
22015e7d8541SAndrew Gallatin 	/* make sure the DMA has finished */
22025e7d8541SAndrew Gallatin 	if (!stats->valid) {
22035e7d8541SAndrew Gallatin 		return;
2204b2fc195eSAndrew Gallatin 	}
22055e7d8541SAndrew Gallatin 	valid = stats->valid;
2206b2fc195eSAndrew Gallatin 
2207dc8731d4SAndrew Gallatin 	if (!sc->msi_enabled) {
22085e7d8541SAndrew Gallatin 		/* lower legacy IRQ  */
22095e7d8541SAndrew Gallatin 		*sc->irq_deassert = 0;
22105e7d8541SAndrew Gallatin 		if (!mxge_deassert_wait)
22115e7d8541SAndrew Gallatin 			/* don't wait for conf. that irq is low */
22125e7d8541SAndrew Gallatin 			stats->valid = 0;
2213dc8731d4SAndrew Gallatin 	} else {
2214dc8731d4SAndrew Gallatin 		stats->valid = 0;
2215dc8731d4SAndrew Gallatin 	}
2216dc8731d4SAndrew Gallatin 
2217dc8731d4SAndrew Gallatin 	/* loop while waiting for legacy irq deassertion */
22185e7d8541SAndrew Gallatin 	do {
22195e7d8541SAndrew Gallatin 		/* check for transmit completes and receives */
22205e7d8541SAndrew Gallatin 		send_done_count = be32toh(stats->send_done_count);
22215e7d8541SAndrew Gallatin 		while ((send_done_count != tx->pkt_done) ||
22225e7d8541SAndrew Gallatin 		       (rx_done->entry[rx_done->idx].length != 0)) {
22235e7d8541SAndrew Gallatin 			mxge_tx_done(sc, (int)send_done_count);
22245e7d8541SAndrew Gallatin 			mxge_clean_rx_done(sc);
22255e7d8541SAndrew Gallatin 			send_done_count = be32toh(stats->send_done_count);
2226b2fc195eSAndrew Gallatin 		}
22275e7d8541SAndrew Gallatin 	} while (*((volatile uint8_t *) &stats->valid));
2228b2fc195eSAndrew Gallatin 
22295e7d8541SAndrew Gallatin 	if (__predict_false(stats->stats_updated)) {
22305e7d8541SAndrew Gallatin 		if (sc->link_state != stats->link_up) {
22315e7d8541SAndrew Gallatin 			sc->link_state = stats->link_up;
2232b2fc195eSAndrew Gallatin 			if (sc->link_state) {
22335e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_UP);
22345e7d8541SAndrew Gallatin 				if (mxge_verbose)
22355e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link up\n");
2236b2fc195eSAndrew Gallatin 			} else {
22375e7d8541SAndrew Gallatin 				if_link_state_change(sc->ifp, LINK_STATE_DOWN);
22385e7d8541SAndrew Gallatin 				if (mxge_verbose)
22395e7d8541SAndrew Gallatin 					device_printf(sc->dev, "link down\n");
2240b2fc195eSAndrew Gallatin 			}
2241b2fc195eSAndrew Gallatin 		}
2242b2fc195eSAndrew Gallatin 		if (sc->rdma_tags_available !=
2243b2fc195eSAndrew Gallatin 		    be32toh(sc->fw_stats->rdma_tags_available)) {
2244b2fc195eSAndrew Gallatin 			sc->rdma_tags_available =
2245b2fc195eSAndrew Gallatin 				be32toh(sc->fw_stats->rdma_tags_available);
22465e7d8541SAndrew Gallatin 			device_printf(sc->dev, "RDMA timed out! %d tags "
22475e7d8541SAndrew Gallatin 				      "left\n", sc->rdma_tags_available);
22485e7d8541SAndrew Gallatin 		}
22495e7d8541SAndrew Gallatin 		sc->down_cnt += stats->link_down;
2250b2fc195eSAndrew Gallatin 	}
2251b2fc195eSAndrew Gallatin 
22525e7d8541SAndrew Gallatin 	/* check to see if we have rx token to pass back */
22535e7d8541SAndrew Gallatin 	if (valid & 0x1)
22545e7d8541SAndrew Gallatin 	    *sc->irq_claim = be32toh(3);
22555e7d8541SAndrew Gallatin 	*(sc->irq_claim + 1) = be32toh(3);
2256b2fc195eSAndrew Gallatin }
2257b2fc195eSAndrew Gallatin 
2258b2fc195eSAndrew Gallatin static void
22596d87a65dSAndrew Gallatin mxge_init(void *arg)
2260b2fc195eSAndrew Gallatin {
2261b2fc195eSAndrew Gallatin }
2262b2fc195eSAndrew Gallatin 
2263b2fc195eSAndrew Gallatin 
2264b2fc195eSAndrew Gallatin 
2265b2fc195eSAndrew Gallatin static void
22666d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc)
2267b2fc195eSAndrew Gallatin {
2268b2fc195eSAndrew Gallatin 	int i;
2269b2fc195eSAndrew Gallatin 
2270b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2271b2fc195eSAndrew Gallatin 		if (sc->rx_big.info[i].m == NULL)
2272b2fc195eSAndrew Gallatin 			continue;
2273b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->rx_big.dmat,
2274b2fc195eSAndrew Gallatin 				  sc->rx_big.info[i].map);
2275b2fc195eSAndrew Gallatin 		m_freem(sc->rx_big.info[i].m);
2276b2fc195eSAndrew Gallatin 		sc->rx_big.info[i].m = NULL;
2277b2fc195eSAndrew Gallatin 	}
2278b2fc195eSAndrew Gallatin 
22799b03b0f3SAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
22809b03b0f3SAndrew Gallatin 		if (sc->rx_small.info[i].m == NULL)
2281b2fc195eSAndrew Gallatin 			continue;
22829b03b0f3SAndrew Gallatin 		bus_dmamap_unload(sc->rx_small.dmat,
22839b03b0f3SAndrew Gallatin 				  sc->rx_small.info[i].map);
22849b03b0f3SAndrew Gallatin 		m_freem(sc->rx_small.info[i].m);
22859b03b0f3SAndrew Gallatin 		sc->rx_small.info[i].m = NULL;
2286b2fc195eSAndrew Gallatin 	}
2287b2fc195eSAndrew Gallatin 
2288b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2289dce01b9bSAndrew Gallatin 		sc->tx.info[i].flag = 0;
2290b2fc195eSAndrew Gallatin 		if (sc->tx.info[i].m == NULL)
2291b2fc195eSAndrew Gallatin 			continue;
2292b2fc195eSAndrew Gallatin 		bus_dmamap_unload(sc->tx.dmat,
2293b2fc195eSAndrew Gallatin 				  sc->tx.info[i].map);
2294b2fc195eSAndrew Gallatin 		m_freem(sc->tx.info[i].m);
2295b2fc195eSAndrew Gallatin 		sc->tx.info[i].m = NULL;
2296b2fc195eSAndrew Gallatin 	}
2297b2fc195eSAndrew Gallatin }
2298b2fc195eSAndrew Gallatin 
2299b2fc195eSAndrew Gallatin static void
23006d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc)
2301b2fc195eSAndrew Gallatin {
2302b2fc195eSAndrew Gallatin 	int i;
2303b2fc195eSAndrew Gallatin 
2304aed8e389SAndrew Gallatin 	if (sc->tx.req_bytes != NULL)
2305b2fc195eSAndrew Gallatin 		free(sc->tx.req_bytes, M_DEVBUF);
2306aed8e389SAndrew Gallatin 	if (sc->tx.seg_list != NULL)
2307aed8e389SAndrew Gallatin 		free(sc->tx.seg_list, M_DEVBUF);
2308b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow != NULL)
2309b2fc195eSAndrew Gallatin 		free(sc->rx_small.shadow, M_DEVBUF);
2310b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow != NULL)
2311b2fc195eSAndrew Gallatin 		free(sc->rx_big.shadow, M_DEVBUF);
2312b2fc195eSAndrew Gallatin 	if (sc->tx.info != NULL) {
2313c2657176SAndrew Gallatin 		if (sc->tx.dmat != NULL) {
2314b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->tx.mask; i++) {
2315b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->tx.dmat,
2316b2fc195eSAndrew Gallatin 						   sc->tx.info[i].map);
2317b2fc195eSAndrew Gallatin 			}
2318c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->tx.dmat);
2319c2657176SAndrew Gallatin 		}
2320b2fc195eSAndrew Gallatin 		free(sc->tx.info, M_DEVBUF);
2321b2fc195eSAndrew Gallatin 	}
2322b2fc195eSAndrew Gallatin 	if (sc->rx_small.info != NULL) {
2323c2657176SAndrew Gallatin 		if (sc->rx_small.dmat != NULL) {
2324b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_small.mask; i++) {
2325b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_small.dmat,
2326b2fc195eSAndrew Gallatin 						   sc->rx_small.info[i].map);
2327b2fc195eSAndrew Gallatin 			}
2328c2657176SAndrew Gallatin 			bus_dmamap_destroy(sc->rx_small.dmat,
2329c2657176SAndrew Gallatin 					   sc->rx_small.extra_map);
2330c2657176SAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_small.dmat);
2331c2657176SAndrew Gallatin 		}
2332b2fc195eSAndrew Gallatin 		free(sc->rx_small.info, M_DEVBUF);
2333b2fc195eSAndrew Gallatin 	}
2334b2fc195eSAndrew Gallatin 	if (sc->rx_big.info != NULL) {
2335c2657176SAndrew Gallatin 		if (sc->rx_big.dmat != NULL) {
2336b2fc195eSAndrew Gallatin 			for (i = 0; i <= sc->rx_big.mask; i++) {
2337b2fc195eSAndrew Gallatin 				bus_dmamap_destroy(sc->rx_big.dmat,
2338b2fc195eSAndrew Gallatin 						   sc->rx_big.info[i].map);
2339b2fc195eSAndrew Gallatin 			}
2340b2fc195eSAndrew Gallatin 			bus_dmamap_destroy(sc->rx_big.dmat,
2341b2fc195eSAndrew Gallatin 					   sc->rx_big.extra_map);
2342b2fc195eSAndrew Gallatin 			bus_dma_tag_destroy(sc->rx_big.dmat);
2343b2fc195eSAndrew Gallatin 		}
2344c2657176SAndrew Gallatin 		free(sc->rx_big.info, M_DEVBUF);
2345c2657176SAndrew Gallatin 	}
2346c2657176SAndrew Gallatin }
2347b2fc195eSAndrew Gallatin 
2348b2fc195eSAndrew Gallatin static int
23496d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc)
2350b2fc195eSAndrew Gallatin {
23516d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2352b2fc195eSAndrew Gallatin 	int tx_ring_size, rx_ring_size;
2353b2fc195eSAndrew Gallatin 	int tx_ring_entries, rx_ring_entries;
2354b2fc195eSAndrew Gallatin 	int i, err;
2355b2fc195eSAndrew Gallatin 	unsigned long bytes;
2356b2fc195eSAndrew Gallatin 
2357b2fc195eSAndrew Gallatin 	/* get ring sizes */
23585e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
2359b2fc195eSAndrew Gallatin 	tx_ring_size = cmd.data0;
23605e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
2361b2fc195eSAndrew Gallatin 	if (err != 0) {
2362b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Cannot determine ring sizes\n");
2363b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2364b2fc195eSAndrew Gallatin 	}
2365b2fc195eSAndrew Gallatin 
2366b2fc195eSAndrew Gallatin 	rx_ring_size = cmd.data0;
2367b2fc195eSAndrew Gallatin 
2368b2fc195eSAndrew Gallatin 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
2369b2fc195eSAndrew Gallatin 	rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t);
237076bb9c5eSAndrew Gallatin 	IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1);
2371a82c2581SAndrew Gallatin 	sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen;
237276bb9c5eSAndrew Gallatin 	IFQ_SET_READY(&sc->ifp->if_snd);
2373b2fc195eSAndrew Gallatin 
2374b2fc195eSAndrew Gallatin 	sc->tx.mask = tx_ring_entries - 1;
2375b2fc195eSAndrew Gallatin 	sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1;
2376b2fc195eSAndrew Gallatin 
2377b2fc195eSAndrew Gallatin 	err = ENOMEM;
2378b2fc195eSAndrew Gallatin 
2379b2fc195eSAndrew Gallatin 	/* allocate the tx request copy block */
2380b2fc195eSAndrew Gallatin 	bytes = 8 +
23815e7d8541SAndrew Gallatin 		sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4);
2382b2fc195eSAndrew Gallatin 	sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK);
2383b2fc195eSAndrew Gallatin 	if (sc->tx.req_bytes == NULL)
2384b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
2385b2fc195eSAndrew Gallatin 	/* ensure req_list entries are aligned to 8 bytes */
2386b2fc195eSAndrew Gallatin 	sc->tx.req_list = (mcp_kreq_ether_send_t *)
2387b2fc195eSAndrew Gallatin 		((unsigned long)(sc->tx.req_bytes + 7) & ~7UL);
2388b2fc195eSAndrew Gallatin 
2389aed8e389SAndrew Gallatin 	/* allocate the tx busdma segment list */
2390aed8e389SAndrew Gallatin 	bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC;
2391aed8e389SAndrew Gallatin 	sc->tx.seg_list = (bus_dma_segment_t *)
2392aed8e389SAndrew Gallatin 		malloc(bytes, M_DEVBUF, M_WAITOK);
2393aed8e389SAndrew Gallatin 	if (sc->tx.seg_list == NULL)
2394aed8e389SAndrew Gallatin 		goto abort_with_alloc;
2395aed8e389SAndrew Gallatin 
2396b2fc195eSAndrew Gallatin 	/* allocate the rx shadow rings */
2397b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow);
2398b2fc195eSAndrew Gallatin 	sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2399b2fc195eSAndrew Gallatin 	if (sc->rx_small.shadow == NULL)
2400b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2401b2fc195eSAndrew Gallatin 
2402b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow);
2403b2fc195eSAndrew Gallatin 	sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2404b2fc195eSAndrew Gallatin 	if (sc->rx_big.shadow == NULL)
2405b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2406b2fc195eSAndrew Gallatin 
2407b2fc195eSAndrew Gallatin 	/* allocate the host info rings */
2408b2fc195eSAndrew Gallatin 	bytes = tx_ring_entries * sizeof (*sc->tx.info);
2409b2fc195eSAndrew Gallatin 	sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2410b2fc195eSAndrew Gallatin 	if (sc->tx.info == NULL)
2411b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2412b2fc195eSAndrew Gallatin 
2413b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_small.info);
2414b2fc195eSAndrew Gallatin 	sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2415b2fc195eSAndrew Gallatin 	if (sc->rx_small.info == NULL)
2416b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2417b2fc195eSAndrew Gallatin 
2418b2fc195eSAndrew Gallatin 	bytes = rx_ring_entries * sizeof (*sc->rx_big.info);
2419b2fc195eSAndrew Gallatin 	sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
2420b2fc195eSAndrew Gallatin 	if (sc->rx_big.info == NULL)
2421b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2422b2fc195eSAndrew Gallatin 
2423b2fc195eSAndrew Gallatin 	/* allocate the busdma resources */
2424b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2425b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2426b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* boundary */
2427b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2428b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2429b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2430aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
2431aed8e389SAndrew Gallatin 				 MXGE_MAX_SEND_DESC/2,	/* num segs */
2432b2fc195eSAndrew Gallatin 				 sc->tx.boundary,	/* maxsegsize */
2433b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2434b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2435b2fc195eSAndrew Gallatin 				 &sc->tx.dmat);		/* tag */
2436b2fc195eSAndrew Gallatin 
2437b2fc195eSAndrew Gallatin 	if (err != 0) {
2438b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
2439b2fc195eSAndrew Gallatin 			      err);
2440b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2441b2fc195eSAndrew Gallatin 	}
2442b2fc195eSAndrew Gallatin 
2443b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2444b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2445b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2446b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2447b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2448b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2449b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsize */
2450b2fc195eSAndrew Gallatin 				 1,			/* num segs */
2451b2fc195eSAndrew Gallatin 				 MHLEN,			/* maxsegsize */
2452b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2453b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2454b2fc195eSAndrew Gallatin 				 &sc->rx_small.dmat);	/* tag */
2455b2fc195eSAndrew Gallatin 	if (err != 0) {
2456b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
2457b2fc195eSAndrew Gallatin 			      err);
2458b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2459b2fc195eSAndrew Gallatin 	}
2460b2fc195eSAndrew Gallatin 
2461b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2462b2fc195eSAndrew Gallatin 				 1,			/* alignment */
2463b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
2464b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
2465b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
2466b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
2467053e637fSAndrew Gallatin 				 3*4096,		/* maxsize */
2468053e637fSAndrew Gallatin 				 3,			/* num segs */
2469b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
2470b2fc195eSAndrew Gallatin 				 BUS_DMA_ALLOCNOW,	/* flags */
2471b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
2472b2fc195eSAndrew Gallatin 				 &sc->rx_big.dmat);	/* tag */
2473b2fc195eSAndrew Gallatin 	if (err != 0) {
2474b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
2475b2fc195eSAndrew Gallatin 			      err);
2476b2fc195eSAndrew Gallatin 		goto abort_with_alloc;
2477b2fc195eSAndrew Gallatin 	}
2478b2fc195eSAndrew Gallatin 
2479b2fc195eSAndrew Gallatin 	/* now use these tags to setup dmamaps for each slot
2480b2fc195eSAndrew Gallatin 	   in each ring */
2481b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->tx.mask; i++) {
2482b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->tx.dmat, 0,
2483b2fc195eSAndrew Gallatin 					&sc->tx.info[i].map);
2484b2fc195eSAndrew Gallatin 		if (err != 0) {
2485b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  tx dmamap\n",
2486b2fc195eSAndrew Gallatin 			      err);
2487b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2488b2fc195eSAndrew Gallatin 		}
2489b2fc195eSAndrew Gallatin 	}
2490b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2491b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_small.dmat, 0,
2492b2fc195eSAndrew Gallatin 					&sc->rx_small.info[i].map);
2493b2fc195eSAndrew Gallatin 		if (err != 0) {
2494b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
2495b2fc195eSAndrew Gallatin 				      err);
2496b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2497b2fc195eSAndrew Gallatin 		}
2498b2fc195eSAndrew Gallatin 	}
2499b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_small.dmat, 0,
2500b2fc195eSAndrew Gallatin 				&sc->rx_small.extra_map);
2501b2fc195eSAndrew Gallatin 	if (err != 0) {
2502b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
2503b2fc195eSAndrew Gallatin 			      err);
2504b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2505b2fc195eSAndrew Gallatin 	}
2506b2fc195eSAndrew Gallatin 
2507b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2508b2fc195eSAndrew Gallatin 		err = bus_dmamap_create(sc->rx_big.dmat, 0,
2509b2fc195eSAndrew Gallatin 					&sc->rx_big.info[i].map);
2510b2fc195eSAndrew Gallatin 		if (err != 0) {
2511b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
2512b2fc195eSAndrew Gallatin 			      err);
2513b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2514b2fc195eSAndrew Gallatin 		}
2515b2fc195eSAndrew Gallatin 	}
2516b2fc195eSAndrew Gallatin 	err = bus_dmamap_create(sc->rx_big.dmat, 0,
2517b2fc195eSAndrew Gallatin 				&sc->rx_big.extra_map);
2518b2fc195eSAndrew Gallatin 	if (err != 0) {
2519b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
2520b2fc195eSAndrew Gallatin 			      err);
2521b2fc195eSAndrew Gallatin 			goto abort_with_alloc;
2522b2fc195eSAndrew Gallatin 	}
2523b2fc195eSAndrew Gallatin 	return 0;
2524b2fc195eSAndrew Gallatin 
2525b2fc195eSAndrew Gallatin abort_with_alloc:
25266d87a65dSAndrew Gallatin 	mxge_free_rings(sc);
2527b2fc195eSAndrew Gallatin 
2528b2fc195eSAndrew Gallatin abort_with_nothing:
2529b2fc195eSAndrew Gallatin 	return err;
2530b2fc195eSAndrew Gallatin }
2531b2fc195eSAndrew Gallatin 
2532053e637fSAndrew Gallatin static void
2533053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
2534053e637fSAndrew Gallatin {
2535053e637fSAndrew Gallatin 	int bufsize = mtu + ETHER_HDR_LEN + 4 + MXGEFW_PAD;
2536053e637fSAndrew Gallatin 
2537053e637fSAndrew Gallatin 	if (bufsize < MCLBYTES) {
2538053e637fSAndrew Gallatin 		/* easy, everything fits in a single buffer */
2539053e637fSAndrew Gallatin 		*big_buf_size = MCLBYTES;
2540053e637fSAndrew Gallatin 		*cl_size = MCLBYTES;
2541053e637fSAndrew Gallatin 		*nbufs = 1;
2542053e637fSAndrew Gallatin 		return;
2543053e637fSAndrew Gallatin 	}
2544053e637fSAndrew Gallatin 
2545053e637fSAndrew Gallatin 	if (bufsize < MJUMPAGESIZE) {
2546053e637fSAndrew Gallatin 		/* still easy, everything still fits in a single buffer */
2547053e637fSAndrew Gallatin 		*big_buf_size = MJUMPAGESIZE;
2548053e637fSAndrew Gallatin 		*cl_size = MJUMPAGESIZE;
2549053e637fSAndrew Gallatin 		*nbufs = 1;
2550053e637fSAndrew Gallatin 		return;
2551053e637fSAndrew Gallatin 	}
2552053e637fSAndrew Gallatin 	/* now we need to use virtually contiguous buffers */
2553053e637fSAndrew Gallatin 	*cl_size = MJUM9BYTES;
2554053e637fSAndrew Gallatin 	*big_buf_size = 4096;
2555053e637fSAndrew Gallatin 	*nbufs = mtu / 4096 + 1;
2556053e637fSAndrew Gallatin 	/* needs to be a power of two, so round up */
2557053e637fSAndrew Gallatin 	if (*nbufs == 3)
2558053e637fSAndrew Gallatin 		*nbufs = 4;
2559053e637fSAndrew Gallatin }
2560053e637fSAndrew Gallatin 
2561b2fc195eSAndrew Gallatin static int
25626d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc)
2563b2fc195eSAndrew Gallatin {
25646d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2565053e637fSAndrew Gallatin 	int i, err, big_bytes;
2566b2fc195eSAndrew Gallatin 	bus_dmamap_t map;
25670fa7f681SAndrew Gallatin 	bus_addr_t bus;
2568053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
2569b2fc195eSAndrew Gallatin 
2570053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_free);
2571053e637fSAndrew Gallatin 	SLIST_INIT(&sc->lro_active);
2572053e637fSAndrew Gallatin 
2573053e637fSAndrew Gallatin 	for (i = 0; i < sc->lro_cnt; i++) {
2574053e637fSAndrew Gallatin 		lro_entry = (struct lro_entry *)
2575053e637fSAndrew Gallatin 			malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
2576053e637fSAndrew Gallatin 		if (lro_entry == NULL) {
2577053e637fSAndrew Gallatin 			sc->lro_cnt = i;
2578053e637fSAndrew Gallatin 			break;
2579053e637fSAndrew Gallatin 		}
2580053e637fSAndrew Gallatin 		SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next);
2581053e637fSAndrew Gallatin 	}
2582b2fc195eSAndrew Gallatin 
25837d542e2dSAndrew Gallatin 	/* Copy the MAC address in case it was overridden */
25847d542e2dSAndrew Gallatin 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
25857d542e2dSAndrew Gallatin 
25866d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
2587b2fc195eSAndrew Gallatin 	if (err != 0) {
2588b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to reset\n");
2589b2fc195eSAndrew Gallatin 		return EIO;
2590b2fc195eSAndrew Gallatin 	}
2591a98d6cd7SAndrew Gallatin 	bzero(sc->rx_done.entry,
2592a98d6cd7SAndrew Gallatin 	      mxge_max_intr_slots * sizeof(*sc->rx_done.entry));
2593b2fc195eSAndrew Gallatin 
2594053e637fSAndrew Gallatin 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes,
2595053e637fSAndrew Gallatin 			   &sc->rx_big.cl_size, &sc->rx_big.nbufs);
2596b2fc195eSAndrew Gallatin 
2597053e637fSAndrew Gallatin 	cmd.data0 = sc->rx_big.nbufs;
2598053e637fSAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
2599053e637fSAndrew Gallatin 			    &cmd);
2600053e637fSAndrew Gallatin 	/* error is only meaningful if we're trying to set
2601053e637fSAndrew Gallatin 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
2602053e637fSAndrew Gallatin 	if (err && sc->rx_big.nbufs > 1) {
2603053e637fSAndrew Gallatin 		device_printf(sc->dev,
2604053e637fSAndrew Gallatin 			      "Failed to set alway-use-n to %d\n",
2605053e637fSAndrew Gallatin 			      sc->rx_big.nbufs);
2606053e637fSAndrew Gallatin 		return EIO;
2607053e637fSAndrew Gallatin 	}
2608b2fc195eSAndrew Gallatin 	/* get the lanai pointers to the send and receive rings */
2609b2fc195eSAndrew Gallatin 
26105e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
2611b2fc195eSAndrew Gallatin 	sc->tx.lanai =
2612b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
26136d87a65dSAndrew Gallatin 	err |= mxge_send_cmd(sc,
26145e7d8541SAndrew Gallatin 				 MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
2615b2fc195eSAndrew Gallatin 	sc->rx_small.lanai =
2616b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
26175e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
2618b2fc195eSAndrew Gallatin 	sc->rx_big.lanai =
2619b2fc195eSAndrew Gallatin 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
2620b2fc195eSAndrew Gallatin 
2621b2fc195eSAndrew Gallatin 	if (err != 0) {
2622b2fc195eSAndrew Gallatin 		device_printf(sc->dev,
2623b2fc195eSAndrew Gallatin 			      "failed to get ring sizes or locations\n");
2624a98d6cd7SAndrew Gallatin 		return EIO;
2625b2fc195eSAndrew Gallatin 	}
2626b2fc195eSAndrew Gallatin 
2627b2fc195eSAndrew Gallatin 	if (sc->wc) {
26280fa7f681SAndrew Gallatin 		sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4;
26290fa7f681SAndrew Gallatin 		sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL;
26300fa7f681SAndrew Gallatin 		sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG;
2631b2fc195eSAndrew Gallatin 	} else {
2632b2fc195eSAndrew Gallatin 		sc->tx.wc_fifo = 0;
2633b2fc195eSAndrew Gallatin 		sc->rx_small.wc_fifo = 0;
2634b2fc195eSAndrew Gallatin 		sc->rx_big.wc_fifo = 0;
2635b2fc195eSAndrew Gallatin 	}
2636b2fc195eSAndrew Gallatin 
2637b2fc195eSAndrew Gallatin 
2638b2fc195eSAndrew Gallatin 	/* stock receive rings */
2639b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_small.mask; i++) {
2640b2fc195eSAndrew Gallatin 		map = sc->rx_small.info[i].map;
26416d87a65dSAndrew Gallatin 		err = mxge_get_buf_small(sc, map, i);
2642b2fc195eSAndrew Gallatin 		if (err) {
2643b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d smalls\n",
2644b2fc195eSAndrew Gallatin 				      i, sc->rx_small.mask + 1);
2645b2fc195eSAndrew Gallatin 			goto abort;
2646b2fc195eSAndrew Gallatin 		}
2647b2fc195eSAndrew Gallatin 	}
2648b2fc195eSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i++) {
2649053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_low = 0xffffffff;
2650053e637fSAndrew Gallatin 		sc->rx_big.shadow[i].addr_high = 0xffffffff;
2651053e637fSAndrew Gallatin 	}
2652053e637fSAndrew Gallatin 	for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) {
2653b2fc195eSAndrew Gallatin 		map = sc->rx_big.info[i].map;
26546d87a65dSAndrew Gallatin 		err = mxge_get_buf_big(sc, map, i);
2655b2fc195eSAndrew Gallatin 		if (err) {
2656b2fc195eSAndrew Gallatin 			device_printf(sc->dev, "alloced %d/%d bigs\n",
2657b2fc195eSAndrew Gallatin 				      i, sc->rx_big.mask + 1);
2658b2fc195eSAndrew Gallatin 			goto abort;
2659b2fc195eSAndrew Gallatin 		}
2660b2fc195eSAndrew Gallatin 	}
2661b2fc195eSAndrew Gallatin 
2662b2fc195eSAndrew Gallatin 	/* Give the firmware the mtu and the big and small buffer
2663b2fc195eSAndrew Gallatin 	   sizes.  The firmware wants the big buf size to be a power
2664b2fc195eSAndrew Gallatin 	   of two. Luckily, FreeBSD's clusters are powers of two */
2665053e637fSAndrew Gallatin 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + 4;
26665e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
2667b4db9009SAndrew Gallatin 	cmd.data0 = MHLEN - MXGEFW_PAD;
26685e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
2669b2fc195eSAndrew Gallatin 			     &cmd);
2670053e637fSAndrew Gallatin 	cmd.data0 = big_bytes;
26715e7d8541SAndrew Gallatin 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
26720fa7f681SAndrew Gallatin 
26730fa7f681SAndrew Gallatin 	if (err != 0) {
26740fa7f681SAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
26750fa7f681SAndrew Gallatin 		goto abort;
26760fa7f681SAndrew Gallatin 	}
26770fa7f681SAndrew Gallatin 
2678b2fc195eSAndrew Gallatin 	/* Now give him the pointer to the stats block */
26796d87a65dSAndrew Gallatin 	cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr);
26806d87a65dSAndrew Gallatin 	cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr);
26810fa7f681SAndrew Gallatin 	cmd.data2 = sizeof(struct mcp_irq_data);
26820fa7f681SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
26830fa7f681SAndrew Gallatin 
26840fa7f681SAndrew Gallatin 	if (err != 0) {
26850fa7f681SAndrew Gallatin 		bus = sc->fw_stats_dma.bus_addr;
26860fa7f681SAndrew Gallatin 		bus += offsetof(struct mcp_irq_data, send_done_count);
26870fa7f681SAndrew Gallatin 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
26880fa7f681SAndrew Gallatin 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
26890fa7f681SAndrew Gallatin 		err = mxge_send_cmd(sc,
26900fa7f681SAndrew Gallatin 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
26910fa7f681SAndrew Gallatin 				    &cmd);
26920fa7f681SAndrew Gallatin 		/* Firmware cannot support multicast without STATS_DMA_V2 */
26930fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 0;
26940fa7f681SAndrew Gallatin 	} else {
26950fa7f681SAndrew Gallatin 		sc->fw_multicast_support = 1;
26960fa7f681SAndrew Gallatin 	}
2697b2fc195eSAndrew Gallatin 
2698b2fc195eSAndrew Gallatin 	if (err != 0) {
2699b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "failed to setup params\n");
2700b2fc195eSAndrew Gallatin 		goto abort;
2701b2fc195eSAndrew Gallatin 	}
2702b2fc195eSAndrew Gallatin 
2703b2fc195eSAndrew Gallatin 	/* Finally, start the firmware running */
27045e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
2705b2fc195eSAndrew Gallatin 	if (err) {
2706b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring up link\n");
2707b2fc195eSAndrew Gallatin 		goto abort;
2708b2fc195eSAndrew Gallatin 	}
2709b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
2710b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2711b2fc195eSAndrew Gallatin 
2712b2fc195eSAndrew Gallatin 	return 0;
2713b2fc195eSAndrew Gallatin 
2714b2fc195eSAndrew Gallatin 
2715b2fc195eSAndrew Gallatin abort:
27166d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2717a98d6cd7SAndrew Gallatin 
2718b2fc195eSAndrew Gallatin 	return err;
2719b2fc195eSAndrew Gallatin }
2720b2fc195eSAndrew Gallatin 
2721b2fc195eSAndrew Gallatin static int
27226d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc)
2723b2fc195eSAndrew Gallatin {
2724053e637fSAndrew Gallatin 	struct lro_entry *lro_entry;
27256d87a65dSAndrew Gallatin 	mxge_cmd_t cmd;
2726b2fc195eSAndrew Gallatin 	int err, old_down_cnt;
2727b2fc195eSAndrew Gallatin 
2728b2fc195eSAndrew Gallatin 	sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2729b2fc195eSAndrew Gallatin 	old_down_cnt = sc->down_cnt;
2730b2fc195eSAndrew Gallatin 	mb();
27315e7d8541SAndrew Gallatin 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
2732b2fc195eSAndrew Gallatin 	if (err) {
2733b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Couldn't bring down link\n");
2734b2fc195eSAndrew Gallatin 	}
2735b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2736b2fc195eSAndrew Gallatin 		/* wait for down irq */
2737dce01b9bSAndrew Gallatin 		DELAY(10 * sc->intr_coal_delay);
2738b2fc195eSAndrew Gallatin 	}
2739b2fc195eSAndrew Gallatin 	if (old_down_cnt == sc->down_cnt) {
2740b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "never got down irq\n");
2741b2fc195eSAndrew Gallatin 	}
2742a98d6cd7SAndrew Gallatin 
27436d87a65dSAndrew Gallatin 	mxge_free_mbufs(sc);
2744a98d6cd7SAndrew Gallatin 
2745053e637fSAndrew Gallatin 	while (!SLIST_EMPTY(&sc->lro_free)) {
2746053e637fSAndrew Gallatin 		lro_entry = SLIST_FIRST(&sc->lro_free);
2747053e637fSAndrew Gallatin 		SLIST_REMOVE_HEAD(&sc->lro_free, next);
2748053e637fSAndrew Gallatin 	}
2749b2fc195eSAndrew Gallatin 	return 0;
2750b2fc195eSAndrew Gallatin }
2751b2fc195eSAndrew Gallatin 
2752dce01b9bSAndrew Gallatin static void
2753dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc)
2754dce01b9bSAndrew Gallatin {
2755dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2756dce01b9bSAndrew Gallatin 	int reg;
2757dce01b9bSAndrew Gallatin 	uint16_t cmd, lnk, pectl;
2758dce01b9bSAndrew Gallatin 
2759dce01b9bSAndrew Gallatin 	/* find the PCIe link width and set max read request to 4KB*/
2760dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
2761dce01b9bSAndrew Gallatin 		lnk = pci_read_config(dev, reg + 0x12, 2);
2762dce01b9bSAndrew Gallatin 		sc->link_width = (lnk >> 4) & 0x3f;
2763dce01b9bSAndrew Gallatin 
2764dce01b9bSAndrew Gallatin 		pectl = pci_read_config(dev, reg + 0x8, 2);
2765dce01b9bSAndrew Gallatin 		pectl = (pectl & ~0x7000) | (5 << 12);
2766dce01b9bSAndrew Gallatin 		pci_write_config(dev, reg + 0x8, pectl, 2);
2767dce01b9bSAndrew Gallatin 	}
2768dce01b9bSAndrew Gallatin 
2769dce01b9bSAndrew Gallatin 	/* Enable DMA and Memory space access */
2770dce01b9bSAndrew Gallatin 	pci_enable_busmaster(dev);
2771dce01b9bSAndrew Gallatin 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
2772dce01b9bSAndrew Gallatin 	cmd |= PCIM_CMD_MEMEN;
2773dce01b9bSAndrew Gallatin 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
2774dce01b9bSAndrew Gallatin }
2775dce01b9bSAndrew Gallatin 
2776dce01b9bSAndrew Gallatin static uint32_t
2777dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc)
2778dce01b9bSAndrew Gallatin {
2779dce01b9bSAndrew Gallatin 	device_t dev = sc->dev;
2780dce01b9bSAndrew Gallatin 	uint32_t vs;
2781dce01b9bSAndrew Gallatin 
2782dce01b9bSAndrew Gallatin 	/* find the vendor specific offset */
2783dce01b9bSAndrew Gallatin 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
2784dce01b9bSAndrew Gallatin 		device_printf(sc->dev,
2785dce01b9bSAndrew Gallatin 			      "could not find vendor specific offset\n");
2786dce01b9bSAndrew Gallatin 		return (uint32_t)-1;
2787dce01b9bSAndrew Gallatin 	}
2788dce01b9bSAndrew Gallatin 	/* enable read32 mode */
2789dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x10, 0x3, 1);
2790dce01b9bSAndrew Gallatin 	/* tell NIC which register to read */
2791dce01b9bSAndrew Gallatin 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
2792dce01b9bSAndrew Gallatin 	return (pci_read_config(dev, vs + 0x14, 4));
2793dce01b9bSAndrew Gallatin }
2794dce01b9bSAndrew Gallatin 
2795dce01b9bSAndrew Gallatin static void
2796dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc)
2797dce01b9bSAndrew Gallatin {
2798dce01b9bSAndrew Gallatin 	int err;
2799dce01b9bSAndrew Gallatin 	uint32_t reboot;
2800dce01b9bSAndrew Gallatin 	uint16_t cmd;
2801dce01b9bSAndrew Gallatin 
2802dce01b9bSAndrew Gallatin 	err = ENXIO;
2803dce01b9bSAndrew Gallatin 
2804dce01b9bSAndrew Gallatin 	device_printf(sc->dev, "Watchdog reset!\n");
2805dce01b9bSAndrew Gallatin 
2806dce01b9bSAndrew Gallatin 	/*
2807dce01b9bSAndrew Gallatin 	 * check to see if the NIC rebooted.  If it did, then all of
2808dce01b9bSAndrew Gallatin 	 * PCI config space has been reset, and things like the
2809dce01b9bSAndrew Gallatin 	 * busmaster bit will be zero.  If this is the case, then we
2810dce01b9bSAndrew Gallatin 	 * must restore PCI config space before the NIC can be used
2811dce01b9bSAndrew Gallatin 	 * again
2812dce01b9bSAndrew Gallatin 	 */
2813dce01b9bSAndrew Gallatin 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2814dce01b9bSAndrew Gallatin 	if (cmd == 0xffff) {
2815dce01b9bSAndrew Gallatin 		/*
2816dce01b9bSAndrew Gallatin 		 * maybe the watchdog caught the NIC rebooting; wait
2817dce01b9bSAndrew Gallatin 		 * up to 100ms for it to finish.  If it does not come
2818dce01b9bSAndrew Gallatin 		 * back, then give up
2819dce01b9bSAndrew Gallatin 		 */
2820dce01b9bSAndrew Gallatin 		DELAY(1000*100);
2821dce01b9bSAndrew Gallatin 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
2822dce01b9bSAndrew Gallatin 		if (cmd == 0xffff) {
2823dce01b9bSAndrew Gallatin 			device_printf(sc->dev, "NIC disappeared!\n");
2824dce01b9bSAndrew Gallatin 			goto abort;
2825dce01b9bSAndrew Gallatin 		}
2826dce01b9bSAndrew Gallatin 	}
2827dce01b9bSAndrew Gallatin 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
2828dce01b9bSAndrew Gallatin 		/* print the reboot status */
2829dce01b9bSAndrew Gallatin 		reboot = mxge_read_reboot(sc);
2830dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
2831dce01b9bSAndrew Gallatin 			      reboot);
2832dce01b9bSAndrew Gallatin 		/* restore PCI configuration space */
2833dce01b9bSAndrew Gallatin 
2834dce01b9bSAndrew Gallatin 		/* XXXX waiting for pci_cfg_restore() to be exported */
2835dce01b9bSAndrew Gallatin 		goto abort; /* just abort for now */
2836dce01b9bSAndrew Gallatin 
2837dce01b9bSAndrew Gallatin 		/* and redo any changes we made to our config space */
2838dce01b9bSAndrew Gallatin 		mxge_setup_cfg_space(sc);
2839dce01b9bSAndrew Gallatin 	} else {
2840dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "NIC did not reboot, ring state:\n");
2841dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "tx.req=%d tx.done=%d\n",
2842dce01b9bSAndrew Gallatin 			      sc->tx.req, sc->tx.done);
2843dce01b9bSAndrew Gallatin 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
2844dce01b9bSAndrew Gallatin 			      sc->tx.pkt_done,
2845dce01b9bSAndrew Gallatin 			      be32toh(sc->fw_stats->send_done_count));
2846dce01b9bSAndrew Gallatin 	}
2847dce01b9bSAndrew Gallatin 
2848dce01b9bSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
2849dce01b9bSAndrew Gallatin 		mxge_close(sc);
2850dce01b9bSAndrew Gallatin 		err = mxge_open(sc);
2851dce01b9bSAndrew Gallatin 	}
2852dce01b9bSAndrew Gallatin 
2853dce01b9bSAndrew Gallatin abort:
2854dce01b9bSAndrew Gallatin 	/*
2855dce01b9bSAndrew Gallatin 	 * stop the watchdog if the nic is dead, to avoid spamming the
2856dce01b9bSAndrew Gallatin 	 * console
2857dce01b9bSAndrew Gallatin 	 */
2858dce01b9bSAndrew Gallatin 	if (err != 0) {
2859dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
2860dce01b9bSAndrew Gallatin 	}
2861dce01b9bSAndrew Gallatin }
2862dce01b9bSAndrew Gallatin 
2863dce01b9bSAndrew Gallatin static void
2864dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc)
2865dce01b9bSAndrew Gallatin {
2866dce01b9bSAndrew Gallatin 	mxge_tx_buf_t *tx = &sc->tx;
2867dce01b9bSAndrew Gallatin 
2868dce01b9bSAndrew Gallatin 	/* see if we have outstanding transmits, which
2869dce01b9bSAndrew Gallatin 	   have been pending for more than mxge_ticks */
2870dce01b9bSAndrew Gallatin 	if (tx->req != tx->done &&
2871dce01b9bSAndrew Gallatin 	    tx->watchdog_req != tx->watchdog_done &&
2872dce01b9bSAndrew Gallatin 	    tx->done == tx->watchdog_done)
2873dce01b9bSAndrew Gallatin 		mxge_watchdog_reset(sc);
2874dce01b9bSAndrew Gallatin 
2875dce01b9bSAndrew Gallatin 	tx->watchdog_req = tx->req;
2876dce01b9bSAndrew Gallatin 	tx->watchdog_done = tx->done;
2877dce01b9bSAndrew Gallatin }
2878dce01b9bSAndrew Gallatin 
2879dce01b9bSAndrew Gallatin static void
2880dce01b9bSAndrew Gallatin mxge_tick(void *arg)
2881dce01b9bSAndrew Gallatin {
2882dce01b9bSAndrew Gallatin 	mxge_softc_t *sc = arg;
2883dce01b9bSAndrew Gallatin 
2884dce01b9bSAndrew Gallatin 
2885dce01b9bSAndrew Gallatin 	/* Synchronize with possible callout reset/stop. */
2886dce01b9bSAndrew Gallatin 	if (callout_pending(&sc->co_hdl) ||
2887dce01b9bSAndrew Gallatin 	    !callout_active(&sc->co_hdl)) {
2888dce01b9bSAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2889dce01b9bSAndrew Gallatin 		return;
2890dce01b9bSAndrew Gallatin 	}
2891dce01b9bSAndrew Gallatin 
2892dce01b9bSAndrew Gallatin 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2893dce01b9bSAndrew Gallatin 	mxge_watchdog(sc);
2894dce01b9bSAndrew Gallatin }
2895b2fc195eSAndrew Gallatin 
2896b2fc195eSAndrew Gallatin static int
28976d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp)
2898b2fc195eSAndrew Gallatin {
2899b2fc195eSAndrew Gallatin 	return EINVAL;
2900b2fc195eSAndrew Gallatin }
2901b2fc195eSAndrew Gallatin 
2902b2fc195eSAndrew Gallatin static int
29036d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu)
2904b2fc195eSAndrew Gallatin {
2905b2fc195eSAndrew Gallatin 	struct ifnet *ifp = sc->ifp;
2906b2fc195eSAndrew Gallatin 	int real_mtu, old_mtu;
2907b2fc195eSAndrew Gallatin 	int err = 0;
2908b2fc195eSAndrew Gallatin 
2909b2fc195eSAndrew Gallatin 
2910b2fc195eSAndrew Gallatin 	real_mtu = mtu + ETHER_HDR_LEN;
2911053e637fSAndrew Gallatin 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
2912b2fc195eSAndrew Gallatin 		return EINVAL;
2913a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
2914b2fc195eSAndrew Gallatin 	old_mtu = ifp->if_mtu;
2915b2fc195eSAndrew Gallatin 	ifp->if_mtu = mtu;
2916b2fc195eSAndrew Gallatin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2917dce01b9bSAndrew Gallatin 		callout_stop(&sc->co_hdl);
29186d87a65dSAndrew Gallatin 		mxge_close(sc);
29196d87a65dSAndrew Gallatin 		err = mxge_open(sc);
2920b2fc195eSAndrew Gallatin 		if (err != 0) {
2921b2fc195eSAndrew Gallatin 			ifp->if_mtu = old_mtu;
29226d87a65dSAndrew Gallatin 			mxge_close(sc);
29236d87a65dSAndrew Gallatin 			(void) mxge_open(sc);
2924b2fc195eSAndrew Gallatin 		}
2925dce01b9bSAndrew Gallatin 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
2926b2fc195eSAndrew Gallatin 	}
2927a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
2928b2fc195eSAndrew Gallatin 	return err;
2929b2fc195eSAndrew Gallatin }
2930b2fc195eSAndrew Gallatin 
2931b2fc195eSAndrew Gallatin static void
29326d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
2933b2fc195eSAndrew Gallatin {
29346d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2935b2fc195eSAndrew Gallatin 
2936b2fc195eSAndrew Gallatin 
2937b2fc195eSAndrew Gallatin 	if (sc == NULL)
2938b2fc195eSAndrew Gallatin 		return;
2939b2fc195eSAndrew Gallatin 	ifmr->ifm_status = IFM_AVALID;
2940b2fc195eSAndrew Gallatin 	ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0;
2941b2fc195eSAndrew Gallatin 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
2942b2fc195eSAndrew Gallatin 	ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0;
2943b2fc195eSAndrew Gallatin }
2944b2fc195eSAndrew Gallatin 
2945b2fc195eSAndrew Gallatin static int
29466d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
2947b2fc195eSAndrew Gallatin {
29486d87a65dSAndrew Gallatin 	mxge_softc_t *sc = ifp->if_softc;
2949b2fc195eSAndrew Gallatin 	struct ifreq *ifr = (struct ifreq *)data;
2950b2fc195eSAndrew Gallatin 	int err, mask;
2951b2fc195eSAndrew Gallatin 
2952b2fc195eSAndrew Gallatin 	err = 0;
2953b2fc195eSAndrew Gallatin 	switch (command) {
2954b2fc195eSAndrew Gallatin 	case SIOCSIFADDR:
2955b2fc195eSAndrew Gallatin 	case SIOCGIFADDR:
2956b2fc195eSAndrew Gallatin 		err = ether_ioctl(ifp, command, data);
2957b2fc195eSAndrew Gallatin 		break;
2958b2fc195eSAndrew Gallatin 
2959b2fc195eSAndrew Gallatin 	case SIOCSIFMTU:
29606d87a65dSAndrew Gallatin 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
2961b2fc195eSAndrew Gallatin 		break;
2962b2fc195eSAndrew Gallatin 
2963b2fc195eSAndrew Gallatin 	case SIOCSIFFLAGS:
2964a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2965b2fc195eSAndrew Gallatin 		if (ifp->if_flags & IFF_UP) {
2966dce01b9bSAndrew Gallatin 			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
29676d87a65dSAndrew Gallatin 				err = mxge_open(sc);
2968dce01b9bSAndrew Gallatin 				callout_reset(&sc->co_hdl, mxge_ticks,
2969dce01b9bSAndrew Gallatin 					      mxge_tick, sc);
2970dce01b9bSAndrew Gallatin 			} else {
29710fa7f681SAndrew Gallatin 				/* take care of promis can allmulti
29720fa7f681SAndrew Gallatin 				   flag chages */
29730fa7f681SAndrew Gallatin 				mxge_change_promisc(sc,
29740fa7f681SAndrew Gallatin 						    ifp->if_flags & IFF_PROMISC);
29750fa7f681SAndrew Gallatin 				mxge_set_multicast_list(sc);
29760fa7f681SAndrew Gallatin 			}
2977b2fc195eSAndrew Gallatin 		} else {
2978dce01b9bSAndrew Gallatin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
29796d87a65dSAndrew Gallatin 				mxge_close(sc);
2980dce01b9bSAndrew Gallatin 				callout_stop(&sc->co_hdl);
2981dce01b9bSAndrew Gallatin 			}
2982b2fc195eSAndrew Gallatin 		}
2983a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2984b2fc195eSAndrew Gallatin 		break;
2985b2fc195eSAndrew Gallatin 
2986b2fc195eSAndrew Gallatin 	case SIOCADDMULTI:
2987b2fc195eSAndrew Gallatin 	case SIOCDELMULTI:
2988a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
29890fa7f681SAndrew Gallatin 		mxge_set_multicast_list(sc);
2990a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
2991b2fc195eSAndrew Gallatin 		break;
2992b2fc195eSAndrew Gallatin 
2993b2fc195eSAndrew Gallatin 	case SIOCSIFCAP:
2994a98d6cd7SAndrew Gallatin 		mtx_lock(&sc->driver_mtx);
2995b2fc195eSAndrew Gallatin 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
2996b2fc195eSAndrew Gallatin 		if (mask & IFCAP_TXCSUM) {
2997b2fc195eSAndrew Gallatin 			if (IFCAP_TXCSUM & ifp->if_capenable) {
2998aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
2999aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
3000aed8e389SAndrew Gallatin 						      | CSUM_TSO);
3001b2fc195eSAndrew Gallatin 			} else {
3002b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_TXCSUM;
3003b2fc195eSAndrew Gallatin 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
3004b2fc195eSAndrew Gallatin 			}
3005b2fc195eSAndrew Gallatin 		} else if (mask & IFCAP_RXCSUM) {
3006b2fc195eSAndrew Gallatin 			if (IFCAP_RXCSUM & ifp->if_capenable) {
3007b2fc195eSAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_RXCSUM;
30085e7d8541SAndrew Gallatin 				sc->csum_flag = 0;
3009b2fc195eSAndrew Gallatin 			} else {
3010b2fc195eSAndrew Gallatin 				ifp->if_capenable |= IFCAP_RXCSUM;
30115e7d8541SAndrew Gallatin 				sc->csum_flag = 1;
3012b2fc195eSAndrew Gallatin 			}
3013b2fc195eSAndrew Gallatin 		}
3014aed8e389SAndrew Gallatin 		if (mask & IFCAP_TSO4) {
3015aed8e389SAndrew Gallatin 			if (IFCAP_TSO4 & ifp->if_capenable) {
3016aed8e389SAndrew Gallatin 				ifp->if_capenable &= ~IFCAP_TSO4;
3017aed8e389SAndrew Gallatin 				ifp->if_hwassist &= ~CSUM_TSO;
3018aed8e389SAndrew Gallatin 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
3019aed8e389SAndrew Gallatin 				ifp->if_capenable |= IFCAP_TSO4;
3020aed8e389SAndrew Gallatin 				ifp->if_hwassist |= CSUM_TSO;
3021aed8e389SAndrew Gallatin 			} else {
3022aed8e389SAndrew Gallatin 				printf("mxge requires tx checksum offload"
3023aed8e389SAndrew Gallatin 				       " be enabled to use TSO\n");
3024aed8e389SAndrew Gallatin 				err = EINVAL;
3025aed8e389SAndrew Gallatin 			}
3026aed8e389SAndrew Gallatin 		}
3027a98d6cd7SAndrew Gallatin 		mtx_unlock(&sc->driver_mtx);
3028b2fc195eSAndrew Gallatin 		break;
3029b2fc195eSAndrew Gallatin 
3030b2fc195eSAndrew Gallatin 	case SIOCGIFMEDIA:
3031b2fc195eSAndrew Gallatin 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
3032b2fc195eSAndrew Gallatin 				    &sc->media, command);
3033b2fc195eSAndrew Gallatin                 break;
3034b2fc195eSAndrew Gallatin 
3035b2fc195eSAndrew Gallatin 	default:
3036b2fc195eSAndrew Gallatin 		err = ENOTTY;
3037b2fc195eSAndrew Gallatin         }
3038b2fc195eSAndrew Gallatin 	return err;
3039b2fc195eSAndrew Gallatin }
3040b2fc195eSAndrew Gallatin 
3041b2fc195eSAndrew Gallatin static void
30426d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc)
3043b2fc195eSAndrew Gallatin {
3044b2fc195eSAndrew Gallatin 
30456d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
30466d87a65dSAndrew Gallatin 			  &mxge_flow_control);
30476d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
30486d87a65dSAndrew Gallatin 			  &mxge_intr_coal_delay);
30496d87a65dSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
30506d87a65dSAndrew Gallatin 			  &mxge_nvidia_ecrc_enable);
3051d91b1b49SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
3052d91b1b49SAndrew Gallatin 			  &mxge_force_firmware);
30535e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
30545e7d8541SAndrew Gallatin 			  &mxge_deassert_wait);
30555e7d8541SAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.verbose",
30565e7d8541SAndrew Gallatin 			  &mxge_verbose);
3057dce01b9bSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
3058053e637fSAndrew Gallatin 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
3059b2fc195eSAndrew Gallatin 
30605e7d8541SAndrew Gallatin 	if (bootverbose)
30615e7d8541SAndrew Gallatin 		mxge_verbose = 1;
30626d87a65dSAndrew Gallatin 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
30636d87a65dSAndrew Gallatin 		mxge_intr_coal_delay = 30;
3064dce01b9bSAndrew Gallatin 	if (mxge_ticks == 0)
3065dce01b9bSAndrew Gallatin 		mxge_ticks = hz;
30666d87a65dSAndrew Gallatin 	sc->pause = mxge_flow_control;
3067053e637fSAndrew Gallatin 
3068b2fc195eSAndrew Gallatin }
3069b2fc195eSAndrew Gallatin 
3070b2fc195eSAndrew Gallatin static int
30716d87a65dSAndrew Gallatin mxge_attach(device_t dev)
3072b2fc195eSAndrew Gallatin {
30736d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3074b2fc195eSAndrew Gallatin 	struct ifnet *ifp;
3075b2fc195eSAndrew Gallatin 	size_t bytes;
3076dce01b9bSAndrew Gallatin 	int count, rid, err;
3077b2fc195eSAndrew Gallatin 
3078b2fc195eSAndrew Gallatin 	sc->dev = dev;
30796d87a65dSAndrew Gallatin 	mxge_fetch_tunables(sc);
3080b2fc195eSAndrew Gallatin 
3081b2fc195eSAndrew Gallatin 	err = bus_dma_tag_create(NULL,			/* parent */
3082b2fc195eSAndrew Gallatin 				 1,			/* alignment */
3083b2fc195eSAndrew Gallatin 				 4096,			/* boundary */
3084b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* low */
3085b2fc195eSAndrew Gallatin 				 BUS_SPACE_MAXADDR,	/* high */
3086b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* filter */
3087aed8e389SAndrew Gallatin 				 65536 + 256,		/* maxsize */
30885e7d8541SAndrew Gallatin 				 MXGE_MAX_SEND_DESC, 	/* num segs */
3089b2fc195eSAndrew Gallatin 				 4096,			/* maxsegsize */
3090b2fc195eSAndrew Gallatin 				 0,			/* flags */
3091b2fc195eSAndrew Gallatin 				 NULL, NULL,		/* lock */
3092b2fc195eSAndrew Gallatin 				 &sc->parent_dmat);	/* tag */
3093b2fc195eSAndrew Gallatin 
3094b2fc195eSAndrew Gallatin 	if (err != 0) {
3095b2fc195eSAndrew Gallatin 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
3096b2fc195eSAndrew Gallatin 			      err);
3097b2fc195eSAndrew Gallatin 		goto abort_with_nothing;
3098b2fc195eSAndrew Gallatin 	}
3099b2fc195eSAndrew Gallatin 
3100b2fc195eSAndrew Gallatin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
3101b2fc195eSAndrew Gallatin 	if (ifp == NULL) {
3102b2fc195eSAndrew Gallatin 		device_printf(dev, "can not if_alloc()\n");
3103b2fc195eSAndrew Gallatin 		err = ENOSPC;
3104b2fc195eSAndrew Gallatin 		goto abort_with_parent_dmat;
3105b2fc195eSAndrew Gallatin 	}
3106a98d6cd7SAndrew Gallatin 	snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd",
3107a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3108a98d6cd7SAndrew Gallatin 	mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF);
3109a98d6cd7SAndrew Gallatin 	snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx",
3110a98d6cd7SAndrew Gallatin 		 device_get_nameunit(dev));
3111a98d6cd7SAndrew Gallatin 	mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF);
3112a98d6cd7SAndrew Gallatin 	snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name),
3113a98d6cd7SAndrew Gallatin 		 "%s:drv", device_get_nameunit(dev));
3114a98d6cd7SAndrew Gallatin 	mtx_init(&sc->driver_mtx, sc->driver_mtx_name,
3115b2fc195eSAndrew Gallatin 		 MTX_NETWORK_LOCK, MTX_DEF);
3116b2fc195eSAndrew Gallatin 
3117dce01b9bSAndrew Gallatin 	callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0);
3118d91b1b49SAndrew Gallatin 
3119dce01b9bSAndrew Gallatin 	mxge_setup_cfg_space(sc);
3120b2fc195eSAndrew Gallatin 
3121b2fc195eSAndrew Gallatin 	/* Map the board into the kernel */
3122b2fc195eSAndrew Gallatin 	rid = PCIR_BARS;
3123b2fc195eSAndrew Gallatin 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
3124b2fc195eSAndrew Gallatin 					 ~0, 1, RF_ACTIVE);
3125b2fc195eSAndrew Gallatin 	if (sc->mem_res == NULL) {
3126b2fc195eSAndrew Gallatin 		device_printf(dev, "could not map memory\n");
3127b2fc195eSAndrew Gallatin 		err = ENXIO;
3128b2fc195eSAndrew Gallatin 		goto abort_with_lock;
3129b2fc195eSAndrew Gallatin 	}
3130b2fc195eSAndrew Gallatin 	sc->sram = rman_get_virtual(sc->mem_res);
3131b2fc195eSAndrew Gallatin 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
3132b2fc195eSAndrew Gallatin 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
3133b2fc195eSAndrew Gallatin 		device_printf(dev, "impossible memory region size %ld\n",
3134b2fc195eSAndrew Gallatin 			      rman_get_size(sc->mem_res));
3135b2fc195eSAndrew Gallatin 		err = ENXIO;
3136b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3137b2fc195eSAndrew Gallatin 	}
3138b2fc195eSAndrew Gallatin 
3139b2fc195eSAndrew Gallatin 	/* make NULL terminated copy of the EEPROM strings section of
3140b2fc195eSAndrew Gallatin 	   lanai SRAM */
31416d87a65dSAndrew Gallatin 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
3142b2fc195eSAndrew Gallatin 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
3143b2fc195eSAndrew Gallatin 				rman_get_bushandle(sc->mem_res),
31446d87a65dSAndrew Gallatin 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
3145b2fc195eSAndrew Gallatin 				sc->eeprom_strings,
31466d87a65dSAndrew Gallatin 				MXGE_EEPROM_STRINGS_SIZE - 2);
31476d87a65dSAndrew Gallatin 	err = mxge_parse_strings(sc);
3148b2fc195eSAndrew Gallatin 	if (err != 0)
3149b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3150b2fc195eSAndrew Gallatin 
3151b2fc195eSAndrew Gallatin 	/* Enable write combining for efficient use of PCIe bus */
31526d87a65dSAndrew Gallatin 	mxge_enable_wc(sc);
3153b2fc195eSAndrew Gallatin 
3154b2fc195eSAndrew Gallatin 	/* Allocate the out of band dma memory */
31556d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
31566d87a65dSAndrew Gallatin 			     sizeof (mxge_cmd_t), 64);
3157b2fc195eSAndrew Gallatin 	if (err != 0)
3158b2fc195eSAndrew Gallatin 		goto abort_with_mem_res;
3159b2fc195eSAndrew Gallatin 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
31606d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
3161b2fc195eSAndrew Gallatin 	if (err != 0)
3162b2fc195eSAndrew Gallatin 		goto abort_with_cmd_dma;
3163b2fc195eSAndrew Gallatin 
31646d87a65dSAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->fw_stats_dma,
3165b2fc195eSAndrew Gallatin 			     sizeof (*sc->fw_stats), 64);
3166b2fc195eSAndrew Gallatin 	if (err != 0)
3167b2fc195eSAndrew Gallatin 		goto abort_with_zeropad_dma;
31685e7d8541SAndrew Gallatin 	sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr;
3169b2fc195eSAndrew Gallatin 
3170a98d6cd7SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
3171a98d6cd7SAndrew Gallatin 	if (err != 0)
3172a98d6cd7SAndrew Gallatin 		goto abort_with_fw_stats;
3173b2fc195eSAndrew Gallatin 
3174b2fc195eSAndrew Gallatin 	/* allocate interrupt queues */
31755e7d8541SAndrew Gallatin 	bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);
31765e7d8541SAndrew Gallatin 	err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096);
3177b2fc195eSAndrew Gallatin 	if (err != 0)
3178a98d6cd7SAndrew Gallatin 		goto abort_with_dmabench;
31795e7d8541SAndrew Gallatin 	sc->rx_done.entry = sc->rx_done.dma.addr;
31805e7d8541SAndrew Gallatin 	bzero(sc->rx_done.entry, bytes);
3181dc8731d4SAndrew Gallatin 
3182b2fc195eSAndrew Gallatin 	/* Add our ithread  */
3183dc8731d4SAndrew Gallatin 	count = pci_msi_count(dev);
3184dc8731d4SAndrew Gallatin 	if (count == 1 && pci_alloc_msi(dev, &count) == 0) {
3185dc8731d4SAndrew Gallatin 		rid = 1;
3186dc8731d4SAndrew Gallatin 		sc->msi_enabled = 1;
3187dc8731d4SAndrew Gallatin 	} else {
3188b2fc195eSAndrew Gallatin 		rid = 0;
3189dc8731d4SAndrew Gallatin 	}
3190b2fc195eSAndrew Gallatin 	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0,
3191b2fc195eSAndrew Gallatin 					 1, RF_SHAREABLE | RF_ACTIVE);
3192b2fc195eSAndrew Gallatin 	if (sc->irq_res == NULL) {
3193b2fc195eSAndrew Gallatin 		device_printf(dev, "could not alloc interrupt\n");
31945e7d8541SAndrew Gallatin 		goto abort_with_rx_done;
3195b2fc195eSAndrew Gallatin 	}
3196d91b1b49SAndrew Gallatin 	if (mxge_verbose)
3197dc8731d4SAndrew Gallatin 		device_printf(dev, "using %s irq %ld\n",
3198dc8731d4SAndrew Gallatin 			      sc->msi_enabled ? "MSI" : "INTx",
3199dc8731d4SAndrew Gallatin 			      rman_get_start(sc->irq_res));
32008fe615baSAndrew Gallatin 	/* select & load the firmware */
32018fe615baSAndrew Gallatin 	err = mxge_select_firmware(sc);
3202b2fc195eSAndrew Gallatin 	if (err != 0)
3203b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
32045e7d8541SAndrew Gallatin 	sc->intr_coal_delay = mxge_intr_coal_delay;
32056d87a65dSAndrew Gallatin 	err = mxge_reset(sc);
3206b2fc195eSAndrew Gallatin 	if (err != 0)
3207b2fc195eSAndrew Gallatin 		goto abort_with_irq_res;
3208b2fc195eSAndrew Gallatin 
3209a98d6cd7SAndrew Gallatin 	err = mxge_alloc_rings(sc);
3210a98d6cd7SAndrew Gallatin 	if (err != 0) {
3211a98d6cd7SAndrew Gallatin 		device_printf(sc->dev, "failed to allocate rings\n");
3212a98d6cd7SAndrew Gallatin 		goto abort_with_irq_res;
3213a98d6cd7SAndrew Gallatin 	}
3214a98d6cd7SAndrew Gallatin 
3215a98d6cd7SAndrew Gallatin 	err = bus_setup_intr(sc->dev, sc->irq_res,
3216a98d6cd7SAndrew Gallatin 			     INTR_TYPE_NET | INTR_MPSAFE,
3217ef544f63SPaolo Pisati 			     NULL, mxge_intr, sc, &sc->ih);
3218a98d6cd7SAndrew Gallatin 	if (err != 0) {
3219a98d6cd7SAndrew Gallatin 		goto abort_with_rings;
3220a98d6cd7SAndrew Gallatin 	}
3221b2fc195eSAndrew Gallatin 	/* hook into the network stack */
3222b2fc195eSAndrew Gallatin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3223b2fc195eSAndrew Gallatin 	ifp->if_baudrate = 100000000;
3224053e637fSAndrew Gallatin 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4;
3225053e637fSAndrew Gallatin 	sc->max_mtu = mxge_max_mtu(sc);
3226053e637fSAndrew Gallatin 	if (sc->max_mtu >= 9000)
3227053e637fSAndrew Gallatin 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
3228053e637fSAndrew Gallatin 	else
3229053e637fSAndrew Gallatin 		device_printf(dev, "MTU limited to %d.  Install "
3230053e637fSAndrew Gallatin 			      "latest firmware for 9000 byte jumbo support",
3231053e637fSAndrew Gallatin 			      sc->max_mtu - ETHER_HDR_LEN);
3232aed8e389SAndrew Gallatin 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
3233b2fc195eSAndrew Gallatin 	ifp->if_capenable = ifp->if_capabilities;
32345e7d8541SAndrew Gallatin 	sc->csum_flag = 1;
32356d87a65dSAndrew Gallatin         ifp->if_init = mxge_init;
3236b2fc195eSAndrew Gallatin         ifp->if_softc = sc;
3237b2fc195eSAndrew Gallatin         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
32386d87a65dSAndrew Gallatin         ifp->if_ioctl = mxge_ioctl;
32396d87a65dSAndrew Gallatin         ifp->if_start = mxge_start;
3240b2fc195eSAndrew Gallatin 	ether_ifattach(ifp, sc->mac_addr);
3241b2fc195eSAndrew Gallatin 	/* ether_ifattach sets mtu to 1500 */
3242053e637fSAndrew Gallatin 	if (ifp->if_capabilities & IFCAP_JUMBO_MTU)
32436d87a65dSAndrew Gallatin 		ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN;
3244b2fc195eSAndrew Gallatin 
3245b2fc195eSAndrew Gallatin 	/* Initialise the ifmedia structure */
32466d87a65dSAndrew Gallatin 	ifmedia_init(&sc->media, 0, mxge_media_change,
32476d87a65dSAndrew Gallatin 		     mxge_media_status);
3248b2fc195eSAndrew Gallatin 	ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
32496d87a65dSAndrew Gallatin 	mxge_add_sysctls(sc);
3250b2fc195eSAndrew Gallatin 	return 0;
3251b2fc195eSAndrew Gallatin 
3252a98d6cd7SAndrew Gallatin abort_with_rings:
3253a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3254b2fc195eSAndrew Gallatin abort_with_irq_res:
3255dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3256dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3257dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3258dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
32595e7d8541SAndrew Gallatin abort_with_rx_done:
32605e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
32615e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
3262a98d6cd7SAndrew Gallatin abort_with_dmabench:
3263a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
32645e7d8541SAndrew Gallatin abort_with_fw_stats:
32656d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3266b2fc195eSAndrew Gallatin abort_with_zeropad_dma:
32676d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
3268b2fc195eSAndrew Gallatin abort_with_cmd_dma:
32696d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3270b2fc195eSAndrew Gallatin abort_with_mem_res:
3271b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3272b2fc195eSAndrew Gallatin abort_with_lock:
3273b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3274a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3275a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3276a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3277b2fc195eSAndrew Gallatin 	if_free(ifp);
3278b2fc195eSAndrew Gallatin abort_with_parent_dmat:
3279b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3280b2fc195eSAndrew Gallatin 
3281b2fc195eSAndrew Gallatin abort_with_nothing:
3282b2fc195eSAndrew Gallatin 	return err;
3283b2fc195eSAndrew Gallatin }
3284b2fc195eSAndrew Gallatin 
3285b2fc195eSAndrew Gallatin static int
32866d87a65dSAndrew Gallatin mxge_detach(device_t dev)
3287b2fc195eSAndrew Gallatin {
32886d87a65dSAndrew Gallatin 	mxge_softc_t *sc = device_get_softc(dev);
3289b2fc195eSAndrew Gallatin 
3290a98d6cd7SAndrew Gallatin 	mtx_lock(&sc->driver_mtx);
3291b2fc195eSAndrew Gallatin 	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING)
32926d87a65dSAndrew Gallatin 		mxge_close(sc);
3293dce01b9bSAndrew Gallatin 	callout_stop(&sc->co_hdl);
3294a98d6cd7SAndrew Gallatin 	mtx_unlock(&sc->driver_mtx);
3295b2fc195eSAndrew Gallatin 	ether_ifdetach(sc->ifp);
3296dce01b9bSAndrew Gallatin 	ifmedia_removeall(&sc->media);
3297091feecdSAndrew Gallatin 	mxge_dummy_rdma(sc, 0);
3298a98d6cd7SAndrew Gallatin 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
3299a98d6cd7SAndrew Gallatin 	mxge_free_rings(sc);
3300dc8731d4SAndrew Gallatin 	bus_release_resource(dev, SYS_RES_IRQ,
3301dc8731d4SAndrew Gallatin 			     sc->msi_enabled ? 1 : 0, sc->irq_res);
3302dc8731d4SAndrew Gallatin 	if (sc->msi_enabled)
3303dc8731d4SAndrew Gallatin 		pci_release_msi(dev);
3304dc8731d4SAndrew Gallatin 
33055e7d8541SAndrew Gallatin 	sc->rx_done.entry = NULL;
33065e7d8541SAndrew Gallatin 	mxge_dma_free(&sc->rx_done.dma);
33076d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->fw_stats_dma);
3308a98d6cd7SAndrew Gallatin 	mxge_dma_free(&sc->dmabench_dma);
33096d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->zeropad_dma);
33106d87a65dSAndrew Gallatin 	mxge_dma_free(&sc->cmd_dma);
3311b2fc195eSAndrew Gallatin 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
3312b2fc195eSAndrew Gallatin 	pci_disable_busmaster(dev);
3313a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->cmd_mtx);
3314a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->tx_mtx);
3315a98d6cd7SAndrew Gallatin 	mtx_destroy(&sc->driver_mtx);
3316b2fc195eSAndrew Gallatin 	if_free(sc->ifp);
3317b2fc195eSAndrew Gallatin 	bus_dma_tag_destroy(sc->parent_dmat);
3318b2fc195eSAndrew Gallatin 	return 0;
3319b2fc195eSAndrew Gallatin }
3320b2fc195eSAndrew Gallatin 
3321b2fc195eSAndrew Gallatin static int
33226d87a65dSAndrew Gallatin mxge_shutdown(device_t dev)
3323b2fc195eSAndrew Gallatin {
3324b2fc195eSAndrew Gallatin 	return 0;
3325b2fc195eSAndrew Gallatin }
3326b2fc195eSAndrew Gallatin 
3327b2fc195eSAndrew Gallatin /*
3328b2fc195eSAndrew Gallatin   This file uses Myri10GE driver indentation.
3329b2fc195eSAndrew Gallatin 
3330b2fc195eSAndrew Gallatin   Local Variables:
3331b2fc195eSAndrew Gallatin   c-file-style:"linux"
3332b2fc195eSAndrew Gallatin   tab-width:8
3333b2fc195eSAndrew Gallatin   End:
3334b2fc195eSAndrew Gallatin */
3335