xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision e3dc37fa)
18892ea20SAggelos Economopoulos /******************************************************************************
28892ea20SAggelos Economopoulos 
38892ea20SAggelos Economopoulos Copyright (c) 2006-2009, Myricom Inc.
48892ea20SAggelos Economopoulos All rights reserved.
58892ea20SAggelos Economopoulos 
68892ea20SAggelos Economopoulos Redistribution and use in source and binary forms, with or without
78892ea20SAggelos Economopoulos modification, are permitted provided that the following conditions are met:
88892ea20SAggelos Economopoulos 
98892ea20SAggelos Economopoulos  1. Redistributions of source code must retain the above copyright notice,
108892ea20SAggelos Economopoulos     this list of conditions and the following disclaimer.
118892ea20SAggelos Economopoulos 
128892ea20SAggelos Economopoulos  2. Neither the name of the Myricom Inc, nor the names of its
138892ea20SAggelos Economopoulos     contributors may be used to endorse or promote products derived from
148892ea20SAggelos Economopoulos     this software without specific prior written permission.
158892ea20SAggelos Economopoulos 
168892ea20SAggelos Economopoulos THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
178892ea20SAggelos Economopoulos AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188892ea20SAggelos Economopoulos IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198892ea20SAggelos Economopoulos ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
208892ea20SAggelos Economopoulos LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
218892ea20SAggelos Economopoulos CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
228892ea20SAggelos Economopoulos SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
238892ea20SAggelos Economopoulos INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
248892ea20SAggelos Economopoulos CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
258892ea20SAggelos Economopoulos ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
268892ea20SAggelos Economopoulos POSSIBILITY OF SUCH DAMAGE.
278892ea20SAggelos Economopoulos 
288892ea20SAggelos Economopoulos ***************************************************************************/
298892ea20SAggelos Economopoulos 
308892ea20SAggelos Economopoulos #include <sys/cdefs.h>
31b3535a6fSAggelos Economopoulos /*__FBSDID("$FreeBSD: src/sys/dev/mxge/if_mxge.c,v 1.63 2009/06/26 11:45:06 rwatson Exp $");*/
328892ea20SAggelos Economopoulos 
338892ea20SAggelos Economopoulos #include <sys/param.h>
348892ea20SAggelos Economopoulos #include <sys/systm.h>
358892ea20SAggelos Economopoulos #include <sys/linker.h>
368892ea20SAggelos Economopoulos #include <sys/firmware.h>
378892ea20SAggelos Economopoulos #include <sys/endian.h>
3805e71c89SAggelos Economopoulos #include <sys/in_cksum.h>
398892ea20SAggelos Economopoulos #include <sys/sockio.h>
408892ea20SAggelos Economopoulos #include <sys/mbuf.h>
418892ea20SAggelos Economopoulos #include <sys/malloc.h>
428892ea20SAggelos Economopoulos #include <sys/kernel.h>
438892ea20SAggelos Economopoulos #include <sys/module.h>
442e8181d0SAggelos Economopoulos #include <sys/serialize.h>
458892ea20SAggelos Economopoulos #include <sys/socket.h>
468892ea20SAggelos Economopoulos #include <sys/sysctl.h>
478892ea20SAggelos Economopoulos 
488892ea20SAggelos Economopoulos /* count xmits ourselves, rather than via drbr */
498892ea20SAggelos Economopoulos #define NO_SLOW_STATS
508892ea20SAggelos Economopoulos #include <net/if.h>
518892ea20SAggelos Economopoulos #include <net/if_arp.h>
52f2f758dfSAggelos Economopoulos #include <net/ifq_var.h>
538892ea20SAggelos Economopoulos #include <net/ethernet.h>
548892ea20SAggelos Economopoulos #include <net/if_dl.h>
558892ea20SAggelos Economopoulos #include <net/if_media.h>
568892ea20SAggelos Economopoulos 
578892ea20SAggelos Economopoulos #include <net/bpf.h>
588892ea20SAggelos Economopoulos 
598892ea20SAggelos Economopoulos #include <net/if_types.h>
60b3535a6fSAggelos Economopoulos #include <net/vlan/if_vlan_var.h>
618892ea20SAggelos Economopoulos #include <net/zlib.h>
628892ea20SAggelos Economopoulos 
638892ea20SAggelos Economopoulos #include <netinet/in_systm.h>
648892ea20SAggelos Economopoulos #include <netinet/in.h>
658892ea20SAggelos Economopoulos #include <netinet/ip.h>
668892ea20SAggelos Economopoulos #include <netinet/tcp.h>
678892ea20SAggelos Economopoulos 
688892ea20SAggelos Economopoulos #include <sys/bus.h>
698892ea20SAggelos Economopoulos #include <sys/rman.h>
708892ea20SAggelos Economopoulos 
71b3535a6fSAggelos Economopoulos #include <bus/pci/pcireg.h>
72b3535a6fSAggelos Economopoulos #include <bus/pci/pcivar.h>
73b3535a6fSAggelos Economopoulos #include <bus/pci/pci_private.h> /* XXX for pci_cfg_restore */
748892ea20SAggelos Economopoulos 
758892ea20SAggelos Economopoulos #include <vm/vm.h>		/* for pmap_mapdev() */
768892ea20SAggelos Economopoulos #include <vm/pmap.h>
778892ea20SAggelos Economopoulos 
788892ea20SAggelos Economopoulos #if defined(__i386) || defined(__amd64)
798892ea20SAggelos Economopoulos #include <machine/specialreg.h>
808892ea20SAggelos Economopoulos #endif
818892ea20SAggelos Economopoulos 
82b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mxge_mcp.h>
83b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mcp_gen_header.h>
848892ea20SAggelos Economopoulos /*#define MXGE_FAKE_IFP*/
85b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/if_mxge_var.h>
868892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
878892ea20SAggelos Economopoulos #include <sys/buf_ring.h>
888892ea20SAggelos Economopoulos #endif
898892ea20SAggelos Economopoulos 
908892ea20SAggelos Economopoulos #include "opt_inet.h"
918892ea20SAggelos Economopoulos 
928892ea20SAggelos Economopoulos /* tunable params */
938892ea20SAggelos Economopoulos static int mxge_nvidia_ecrc_enable = 1;
948892ea20SAggelos Economopoulos static int mxge_force_firmware = 0;
958892ea20SAggelos Economopoulos static int mxge_intr_coal_delay = 30;
968892ea20SAggelos Economopoulos static int mxge_deassert_wait = 1;
978892ea20SAggelos Economopoulos static int mxge_flow_control = 1;
988892ea20SAggelos Economopoulos static int mxge_verbose = 0;
998892ea20SAggelos Economopoulos static int mxge_lro_cnt = 8;
1008892ea20SAggelos Economopoulos static int mxge_ticks;
1018892ea20SAggelos Economopoulos static int mxge_max_slices = 1;
1028892ea20SAggelos Economopoulos static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
1038892ea20SAggelos Economopoulos static int mxge_always_promisc = 0;
1048892ea20SAggelos Economopoulos static int mxge_initial_mtu = ETHERMTU_JUMBO;
1058892ea20SAggelos Economopoulos static char *mxge_fw_unaligned = "mxge_ethp_z8e";
1068892ea20SAggelos Economopoulos static char *mxge_fw_aligned = "mxge_eth_z8e";
1078892ea20SAggelos Economopoulos static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
1088892ea20SAggelos Economopoulos static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
1098892ea20SAggelos Economopoulos 
1108892ea20SAggelos Economopoulos static int mxge_probe(device_t dev);
1118892ea20SAggelos Economopoulos static int mxge_attach(device_t dev);
1128892ea20SAggelos Economopoulos static int mxge_detach(device_t dev);
1138892ea20SAggelos Economopoulos static int mxge_shutdown(device_t dev);
1148892ea20SAggelos Economopoulos static void mxge_intr(void *arg);
1158892ea20SAggelos Economopoulos 
1168892ea20SAggelos Economopoulos static device_method_t mxge_methods[] =
1178892ea20SAggelos Economopoulos {
1188892ea20SAggelos Economopoulos   /* Device interface */
1198892ea20SAggelos Economopoulos   DEVMETHOD(device_probe, mxge_probe),
1208892ea20SAggelos Economopoulos   DEVMETHOD(device_attach, mxge_attach),
1218892ea20SAggelos Economopoulos   DEVMETHOD(device_detach, mxge_detach),
1228892ea20SAggelos Economopoulos   DEVMETHOD(device_shutdown, mxge_shutdown),
1238892ea20SAggelos Economopoulos   {0, 0}
1248892ea20SAggelos Economopoulos };
1258892ea20SAggelos Economopoulos 
1268892ea20SAggelos Economopoulos static driver_t mxge_driver =
1278892ea20SAggelos Economopoulos {
1288892ea20SAggelos Economopoulos   "mxge",
1298892ea20SAggelos Economopoulos   mxge_methods,
1308892ea20SAggelos Economopoulos   sizeof(mxge_softc_t),
1318892ea20SAggelos Economopoulos };
1328892ea20SAggelos Economopoulos 
1338892ea20SAggelos Economopoulos static devclass_t mxge_devclass;
1348892ea20SAggelos Economopoulos 
1358892ea20SAggelos Economopoulos /* Declare ourselves to be a child of the PCI bus.*/
1368892ea20SAggelos Economopoulos DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0);
1378892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, firmware, 1, 1, 1);
1388892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, zlib, 1, 1, 1);
1398892ea20SAggelos Economopoulos 
1408892ea20SAggelos Economopoulos static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1418892ea20SAggelos Economopoulos static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1428892ea20SAggelos Economopoulos static int mxge_close(mxge_softc_t *sc);
1438892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1448892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
1458892ea20SAggelos Economopoulos 
1468892ea20SAggelos Economopoulos static int
1478892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1488892ea20SAggelos Economopoulos {
1498892ea20SAggelos Economopoulos 	int rev;
1508892ea20SAggelos Economopoulos 
1518892ea20SAggelos Economopoulos 
1528892ea20SAggelos Economopoulos 	if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) &&
1538892ea20SAggelos Economopoulos 	    ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) ||
1548892ea20SAggelos Economopoulos 	     (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) {
1558892ea20SAggelos Economopoulos 		rev = pci_get_revid(dev);
1568892ea20SAggelos Economopoulos 		switch (rev) {
1578892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1588892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1598892ea20SAggelos Economopoulos 			break;
1608892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1618892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1628892ea20SAggelos Economopoulos 			break;
1638892ea20SAggelos Economopoulos 		default:
1648892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
1658892ea20SAggelos Economopoulos 			device_printf(dev, "Unrecognized rev %d NIC\n",
1668892ea20SAggelos Economopoulos 				      rev);
1678892ea20SAggelos Economopoulos 			break;
1688892ea20SAggelos Economopoulos 		}
1698892ea20SAggelos Economopoulos 		return 0;
1708892ea20SAggelos Economopoulos 	}
1718892ea20SAggelos Economopoulos 	return ENXIO;
1728892ea20SAggelos Economopoulos }
1738892ea20SAggelos Economopoulos 
1748892ea20SAggelos Economopoulos static void
1758892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1768892ea20SAggelos Economopoulos {
1779eb279beSAggelos Economopoulos #if 0
1788892ea20SAggelos Economopoulos #if defined(__i386) || defined(__amd64)
1798892ea20SAggelos Economopoulos 	vm_offset_t len;
1808892ea20SAggelos Economopoulos 	int err;
1818892ea20SAggelos Economopoulos 
1828892ea20SAggelos Economopoulos 	sc->wc = 1;
1838892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
1848892ea20SAggelos Economopoulos 	err = pmap_change_attr((vm_offset_t) sc->sram,
1858892ea20SAggelos Economopoulos 			       len, PAT_WRITE_COMBINING);
1868892ea20SAggelos Economopoulos 	if (err != 0) {
1878892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_change_attr failed, %d\n",
1888892ea20SAggelos Economopoulos 			      err);
1898892ea20SAggelos Economopoulos 		sc->wc = 0;
1908892ea20SAggelos Economopoulos 	}
1918892ea20SAggelos Economopoulos #endif
1929eb279beSAggelos Economopoulos #else
1939eb279beSAggelos Economopoulos 	sc->wc = 0;	/* TBD: PAT support */
1949eb279beSAggelos Economopoulos #endif
1958892ea20SAggelos Economopoulos }
1968892ea20SAggelos Economopoulos 
1978892ea20SAggelos Economopoulos 
1988892ea20SAggelos Economopoulos /* callback to get our DMA address */
1998892ea20SAggelos Economopoulos static void
2008892ea20SAggelos Economopoulos mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
2018892ea20SAggelos Economopoulos 			 int error)
2028892ea20SAggelos Economopoulos {
2038892ea20SAggelos Economopoulos 	if (error == 0) {
2048892ea20SAggelos Economopoulos 		*(bus_addr_t *) arg = segs->ds_addr;
2058892ea20SAggelos Economopoulos 	}
2068892ea20SAggelos Economopoulos }
2078892ea20SAggelos Economopoulos 
2088892ea20SAggelos Economopoulos static int
2098892ea20SAggelos Economopoulos mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes,
2108892ea20SAggelos Economopoulos 		   bus_size_t alignment)
2118892ea20SAggelos Economopoulos {
2128892ea20SAggelos Economopoulos 	int err;
2138892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
2148892ea20SAggelos Economopoulos 	bus_size_t boundary, maxsegsize;
2158892ea20SAggelos Economopoulos 
2168892ea20SAggelos Economopoulos 	if (bytes > 4096 && alignment == 4096) {
2178892ea20SAggelos Economopoulos 		boundary = 0;
2188892ea20SAggelos Economopoulos 		maxsegsize = bytes;
2198892ea20SAggelos Economopoulos 	} else {
2208892ea20SAggelos Economopoulos 		boundary = 4096;
2218892ea20SAggelos Economopoulos 		maxsegsize = 4096;
2228892ea20SAggelos Economopoulos 	}
2238892ea20SAggelos Economopoulos 
2248892ea20SAggelos Economopoulos 	/* allocate DMAable memory tags */
2258892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
2268892ea20SAggelos Economopoulos 				 alignment,		/* alignment */
2278892ea20SAggelos Economopoulos 				 boundary,		/* boundary */
2288892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
2298892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
2308892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2318892ea20SAggelos Economopoulos 				 bytes,			/* maxsize */
2328892ea20SAggelos Economopoulos 				 1,			/* num segs */
2338892ea20SAggelos Economopoulos 				 maxsegsize,		/* maxsegsize */
2348892ea20SAggelos Economopoulos 				 BUS_DMA_COHERENT,	/* flags */
2358892ea20SAggelos Economopoulos 				 &dma->dmat);		/* tag */
2368892ea20SAggelos Economopoulos 	if (err != 0) {
2378892ea20SAggelos Economopoulos 		device_printf(dev, "couldn't alloc tag (err = %d)\n", err);
2388892ea20SAggelos Economopoulos 		return err;
2398892ea20SAggelos Economopoulos 	}
2408892ea20SAggelos Economopoulos 
2418892ea20SAggelos Economopoulos 	/* allocate DMAable memory & map */
2428892ea20SAggelos Economopoulos 	err = bus_dmamem_alloc(dma->dmat, &dma->addr,
2438892ea20SAggelos Economopoulos 			       (BUS_DMA_WAITOK | BUS_DMA_COHERENT
2448892ea20SAggelos Economopoulos 				| BUS_DMA_ZERO),  &dma->map);
2458892ea20SAggelos Economopoulos 	if (err != 0) {
2468892ea20SAggelos Economopoulos 		device_printf(dev, "couldn't alloc mem (err = %d)\n", err);
2478892ea20SAggelos Economopoulos 		goto abort_with_dmat;
2488892ea20SAggelos Economopoulos 	}
2498892ea20SAggelos Economopoulos 
2508892ea20SAggelos Economopoulos 	/* load the memory */
2518892ea20SAggelos Economopoulos 	err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes,
2528892ea20SAggelos Economopoulos 			      mxge_dmamap_callback,
2538892ea20SAggelos Economopoulos 			      (void *)&dma->bus_addr, 0);
2548892ea20SAggelos Economopoulos 	if (err != 0) {
2558892ea20SAggelos Economopoulos 		device_printf(dev, "couldn't load map (err = %d)\n", err);
2568892ea20SAggelos Economopoulos 		goto abort_with_mem;
2578892ea20SAggelos Economopoulos 	}
2588892ea20SAggelos Economopoulos 	return 0;
2598892ea20SAggelos Economopoulos 
2608892ea20SAggelos Economopoulos abort_with_mem:
2618892ea20SAggelos Economopoulos 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
2628892ea20SAggelos Economopoulos abort_with_dmat:
2638892ea20SAggelos Economopoulos 	(void)bus_dma_tag_destroy(dma->dmat);
2648892ea20SAggelos Economopoulos 	return err;
2658892ea20SAggelos Economopoulos }
2668892ea20SAggelos Economopoulos 
2678892ea20SAggelos Economopoulos 
2688892ea20SAggelos Economopoulos static void
2698892ea20SAggelos Economopoulos mxge_dma_free(mxge_dma_t *dma)
2708892ea20SAggelos Economopoulos {
2718892ea20SAggelos Economopoulos 	bus_dmamap_unload(dma->dmat, dma->map);
2728892ea20SAggelos Economopoulos 	bus_dmamem_free(dma->dmat, dma->addr, dma->map);
2738892ea20SAggelos Economopoulos 	(void)bus_dma_tag_destroy(dma->dmat);
2748892ea20SAggelos Economopoulos }
2758892ea20SAggelos Economopoulos 
2768892ea20SAggelos Economopoulos /*
2778892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2788892ea20SAggelos Economopoulos  * SN=x\0
2798892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2808892ea20SAggelos Economopoulos  * PC=text\0
2818892ea20SAggelos Economopoulos  */
2828892ea20SAggelos Economopoulos 
2838892ea20SAggelos Economopoulos static int
2848892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2858892ea20SAggelos Economopoulos {
2868892ea20SAggelos Economopoulos #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++)
2878892ea20SAggelos Economopoulos 
2888892ea20SAggelos Economopoulos 	char *ptr, *limit;
2898892ea20SAggelos Economopoulos 	int i, found_mac;
2908892ea20SAggelos Economopoulos 
2918892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2928892ea20SAggelos Economopoulos 	limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE;
2938892ea20SAggelos Economopoulos 	found_mac = 0;
2948892ea20SAggelos Economopoulos 	while (ptr < limit && *ptr != '\0') {
2958892ea20SAggelos Economopoulos 		if (memcmp(ptr, "MAC=", 4) == 0) {
2968892ea20SAggelos Economopoulos 			ptr += 1;
2978892ea20SAggelos Economopoulos 			sc->mac_addr_string = ptr;
2988892ea20SAggelos Economopoulos 			for (i = 0; i < 6; i++) {
2998892ea20SAggelos Economopoulos 				ptr += 3;
3008892ea20SAggelos Economopoulos 				if ((ptr + 2) > limit)
3018892ea20SAggelos Economopoulos 					goto abort;
3028892ea20SAggelos Economopoulos 				sc->mac_addr[i] = strtoul(ptr, NULL, 16);
3038892ea20SAggelos Economopoulos 				found_mac = 1;
3048892ea20SAggelos Economopoulos 			}
3058892ea20SAggelos Economopoulos 		} else if (memcmp(ptr, "PC=", 3) == 0) {
3068892ea20SAggelos Economopoulos 			ptr += 3;
3078892ea20SAggelos Economopoulos 			strncpy(sc->product_code_string, ptr,
3088892ea20SAggelos Economopoulos 				sizeof (sc->product_code_string) - 1);
3098892ea20SAggelos Economopoulos 		} else if (memcmp(ptr, "SN=", 3) == 0) {
3108892ea20SAggelos Economopoulos 			ptr += 3;
3118892ea20SAggelos Economopoulos 			strncpy(sc->serial_number_string, ptr,
3128892ea20SAggelos Economopoulos 				sizeof (sc->serial_number_string) - 1);
3138892ea20SAggelos Economopoulos 		}
3148892ea20SAggelos Economopoulos 		MXGE_NEXT_STRING(ptr);
3158892ea20SAggelos Economopoulos 	}
3168892ea20SAggelos Economopoulos 
3178892ea20SAggelos Economopoulos 	if (found_mac)
3188892ea20SAggelos Economopoulos 		return 0;
3198892ea20SAggelos Economopoulos 
3208892ea20SAggelos Economopoulos  abort:
3218892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
3228892ea20SAggelos Economopoulos 
3238892ea20SAggelos Economopoulos 	return ENXIO;
3248892ea20SAggelos Economopoulos }
3258892ea20SAggelos Economopoulos 
3268892ea20SAggelos Economopoulos #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__
3278892ea20SAggelos Economopoulos static void
3288892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
3298892ea20SAggelos Economopoulos {
3308892ea20SAggelos Economopoulos 	uint32_t val;
3318892ea20SAggelos Economopoulos 	unsigned long base, off;
3328892ea20SAggelos Economopoulos 	char *va, *cfgptr;
3338892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
3348892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
3358892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
3368892ea20SAggelos Economopoulos 	uint32_t *ptr32;
3378892ea20SAggelos Economopoulos 
3388892ea20SAggelos Economopoulos 
3398892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
3408892ea20SAggelos Economopoulos 		return;
3418892ea20SAggelos Economopoulos 
3428892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
3438892ea20SAggelos Economopoulos 	if (pdev == NULL) {
3448892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
3458892ea20SAggelos Economopoulos 		return;
3468892ea20SAggelos Economopoulos 	}
3478892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3488892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3498892ea20SAggelos Economopoulos 
3508892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
3518892ea20SAggelos Economopoulos 		return;
3528892ea20SAggelos Economopoulos 
3538892ea20SAggelos Economopoulos 	base = 0;
3548892ea20SAggelos Economopoulos 
3558892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3568892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3578892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3588892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3598892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3608892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3618892ea20SAggelos Economopoulos 		if (mcp55 &&
3628892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3638892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3648892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3658892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3668892ea20SAggelos Economopoulos 		}
3678892ea20SAggelos Economopoulos 	}
3688892ea20SAggelos Economopoulos 	if (!base)
3698892ea20SAggelos Economopoulos 		return;
3708892ea20SAggelos Economopoulos 
3718892ea20SAggelos Economopoulos 	/* XXXX
3728892ea20SAggelos Economopoulos 	   Test below is commented because it is believed that doing
3738892ea20SAggelos Economopoulos 	   config read/write beyond 0xff will access the config space
3748892ea20SAggelos Economopoulos 	   for the next larger function.  Uncomment this and remove
3758892ea20SAggelos Economopoulos 	   the hacky pmap_mapdev() way of accessing config space when
3768892ea20SAggelos Economopoulos 	   FreeBSD grows support for extended pcie config space access
3778892ea20SAggelos Economopoulos 	*/
3788892ea20SAggelos Economopoulos #if 0
3798892ea20SAggelos Economopoulos 	/* See if we can, by some miracle, access the extended
3808892ea20SAggelos Economopoulos 	   config space */
3818892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3828892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3838892ea20SAggelos Economopoulos 		val |= 0x40;
3848892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3858892ea20SAggelos Economopoulos 		return;
3868892ea20SAggelos Economopoulos 	}
3878892ea20SAggelos Economopoulos #endif
3888892ea20SAggelos Economopoulos 	/* Rather than using normal pci config space writes, we must
3898892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3908892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3918892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3928892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3938892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3948892ea20SAggelos Economopoulos 	 */
3958892ea20SAggelos Economopoulos 
3968892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3978892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3988892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3998892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
4008892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
4018892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
4028892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
4038892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
4048892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
4058892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
4068892ea20SAggelos Economopoulos 
4078892ea20SAggelos Economopoulos 	off =  base
4088892ea20SAggelos Economopoulos 		+ 0x00100000UL * (unsigned long)bus
4098892ea20SAggelos Economopoulos 		+ 0x00001000UL * (unsigned long)(func
4108892ea20SAggelos Economopoulos 						 + 8 * slot);
4118892ea20SAggelos Economopoulos 
4128892ea20SAggelos Economopoulos 	/* map it into the kernel */
4138892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
4148892ea20SAggelos Economopoulos 
4158892ea20SAggelos Economopoulos 
4168892ea20SAggelos Economopoulos 	if (va == NULL) {
4178892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
4188892ea20SAggelos Economopoulos 		return;
4198892ea20SAggelos Economopoulos 	}
4208892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
4218892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
4228892ea20SAggelos Economopoulos 
4238892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
4248892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
4258892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
4268892ea20SAggelos Economopoulos 	if (! (vendor_id == ivend && device_id == idev)) {
4278892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
4288892ea20SAggelos Economopoulos 			      vendor_id, device_id);
4298892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4308892ea20SAggelos Economopoulos 		return;
4318892ea20SAggelos Economopoulos 	}
4328892ea20SAggelos Economopoulos 
4338892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
4348892ea20SAggelos Economopoulos 	val = *ptr32;
4358892ea20SAggelos Economopoulos 
4368892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
4378892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
4388892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4398892ea20SAggelos Economopoulos 		return;
4408892ea20SAggelos Economopoulos 	}
4418892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
4428892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4438892ea20SAggelos Economopoulos 	if (mxge_verbose)
4448892ea20SAggelos Economopoulos 		device_printf(sc->dev,
4458892ea20SAggelos Economopoulos 			      "Enabled ECRC on upstream Nvidia bridge "
4468892ea20SAggelos Economopoulos 			      "at %d:%d:%d\n",
4478892ea20SAggelos Economopoulos 			      (int)bus, (int)slot, (int)func);
4488892ea20SAggelos Economopoulos 	return;
4498892ea20SAggelos Economopoulos }
4508892ea20SAggelos Economopoulos #else
4518892ea20SAggelos Economopoulos static void
4528892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
4538892ea20SAggelos Economopoulos {
4548892ea20SAggelos Economopoulos 	device_printf(sc->dev,
4558892ea20SAggelos Economopoulos 		      "Nforce 4 chipset on non-x86/amd64!?!?!\n");
4568892ea20SAggelos Economopoulos 	return;
4578892ea20SAggelos Economopoulos }
4588892ea20SAggelos Economopoulos #endif
4598892ea20SAggelos Economopoulos 
4608892ea20SAggelos Economopoulos 
4618892ea20SAggelos Economopoulos static int
4628892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4638892ea20SAggelos Economopoulos {
4648892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4658892ea20SAggelos Economopoulos 	bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr;
4668892ea20SAggelos Economopoulos 	int status;
4678892ea20SAggelos Economopoulos 	uint32_t len;
4688892ea20SAggelos Economopoulos 	char *test = " ";
4698892ea20SAggelos Economopoulos 
4708892ea20SAggelos Economopoulos 
4718892ea20SAggelos Economopoulos 	/* Run a small DMA test.
4728892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4738892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4748892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4758892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4768892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4778892ea20SAggelos Economopoulos 	 * transfers took to complete.
4788892ea20SAggelos Economopoulos 	 */
4798892ea20SAggelos Economopoulos 
4808892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4818892ea20SAggelos Economopoulos 
4828892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4838892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4848892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4858892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4868892ea20SAggelos Economopoulos 	if (status != 0) {
4878892ea20SAggelos Economopoulos 		test = "read";
4888892ea20SAggelos Economopoulos 		goto abort;
4898892ea20SAggelos Economopoulos 	}
4908892ea20SAggelos Economopoulos 	sc->read_dma = ((cmd.data0>>16) * len * 2) /
4918892ea20SAggelos Economopoulos 		(cmd.data0 & 0xffff);
4928892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4938892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4948892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4958892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4968892ea20SAggelos Economopoulos 	if (status != 0) {
4978892ea20SAggelos Economopoulos 		test = "write";
4988892ea20SAggelos Economopoulos 		goto abort;
4998892ea20SAggelos Economopoulos 	}
5008892ea20SAggelos Economopoulos 	sc->write_dma = ((cmd.data0>>16) * len * 2) /
5018892ea20SAggelos Economopoulos 		(cmd.data0 & 0xffff);
5028892ea20SAggelos Economopoulos 
5038892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
5048892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
5058892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
5068892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
5078892ea20SAggelos Economopoulos 	if (status != 0) {
5088892ea20SAggelos Economopoulos 		test = "read/write";
5098892ea20SAggelos Economopoulos 		goto abort;
5108892ea20SAggelos Economopoulos 	}
5118892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
5128892ea20SAggelos Economopoulos 		(cmd.data0 & 0xffff);
5138892ea20SAggelos Economopoulos 
5148892ea20SAggelos Economopoulos abort:
5158892ea20SAggelos Economopoulos 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
5168892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
5178892ea20SAggelos Economopoulos 			      test, status);
5188892ea20SAggelos Economopoulos 
5198892ea20SAggelos Economopoulos 	return status;
5208892ea20SAggelos Economopoulos }
5218892ea20SAggelos Economopoulos 
5228892ea20SAggelos Economopoulos /*
5238892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
5248892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
5258892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
5268892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
5278892ea20SAggelos Economopoulos  * ECRC generation (if supported).
5288892ea20SAggelos Economopoulos  *
5298892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
5308892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
5318892ea20SAggelos Economopoulos  *
5328892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
5338892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
5348892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
5358892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
5368892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
5378892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
5388892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
5398892ea20SAggelos Economopoulos  */
5408892ea20SAggelos Economopoulos 
5418892ea20SAggelos Economopoulos static int
5428892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
5438892ea20SAggelos Economopoulos {
5448892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
5458892ea20SAggelos Economopoulos 	int reg, status;
5468892ea20SAggelos Economopoulos 	uint16_t pectl;
5478892ea20SAggelos Economopoulos 
5488892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
5498892ea20SAggelos Economopoulos 	/*
5508892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
5518892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
5528892ea20SAggelos Economopoulos 	 */
5538892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5548892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
5558892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
5568892ea20SAggelos Economopoulos 			device_printf(dev, "Max Read Req. size != 4k (0x%x\n",
5578892ea20SAggelos Economopoulos 				      pectl);
5588892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5598892ea20SAggelos Economopoulos 		}
5608892ea20SAggelos Economopoulos 	}
5618892ea20SAggelos Economopoulos 
5628892ea20SAggelos Economopoulos 	/*
5638892ea20SAggelos Economopoulos 	 * load the optimized firmware (which assumes aligned PCIe
5648892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5658892ea20SAggelos Economopoulos 	 */
5668892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5678892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5688892ea20SAggelos Economopoulos 	if (status != 0) {
5698892ea20SAggelos Economopoulos 		return status;
5708892ea20SAggelos Economopoulos 	}
5718892ea20SAggelos Economopoulos 
5728892ea20SAggelos Economopoulos 	/*
5738892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5748892ea20SAggelos Economopoulos 	 */
5758892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5768892ea20SAggelos Economopoulos 
5778892ea20SAggelos Economopoulos 	/*
5788892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
5798892ea20SAggelos Economopoulos 	 * aborts on the first one seen.
5808892ea20SAggelos Economopoulos 	 */
5818892ea20SAggelos Economopoulos 
5828892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5838892ea20SAggelos Economopoulos 	if (status == 0)
5848892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5858892ea20SAggelos Economopoulos 
5868892ea20SAggelos Economopoulos 	if (status != E2BIG)
5878892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5888892ea20SAggelos Economopoulos 	if (status == ENOSYS)
5898892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5908892ea20SAggelos Economopoulos 			      "Please install up to date fw\n");
5918892ea20SAggelos Economopoulos 	return status;
5928892ea20SAggelos Economopoulos }
5938892ea20SAggelos Economopoulos 
5948892ea20SAggelos Economopoulos static int
5958892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5968892ea20SAggelos Economopoulos {
5978892ea20SAggelos Economopoulos 	int aligned = 0;
5988892ea20SAggelos Economopoulos 
5998892ea20SAggelos Economopoulos 
6008892ea20SAggelos Economopoulos 	if (mxge_force_firmware != 0) {
6018892ea20SAggelos Economopoulos 		if (mxge_force_firmware == 1)
6028892ea20SAggelos Economopoulos 			aligned = 1;
6038892ea20SAggelos Economopoulos 		else
6048892ea20SAggelos Economopoulos 			aligned = 0;
6058892ea20SAggelos Economopoulos 		if (mxge_verbose)
6068892ea20SAggelos Economopoulos 			device_printf(sc->dev,
6078892ea20SAggelos Economopoulos 				      "Assuming %s completions (forced)\n",
6088892ea20SAggelos Economopoulos 				      aligned ? "aligned" : "unaligned");
6098892ea20SAggelos Economopoulos 		goto abort;
6108892ea20SAggelos Economopoulos 	}
6118892ea20SAggelos Economopoulos 
6128892ea20SAggelos Economopoulos 	/* if the PCIe link width is 4 or less, we can use the aligned
6138892ea20SAggelos Economopoulos 	   firmware and skip any checks */
6148892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
6158892ea20SAggelos Economopoulos 		device_printf(sc->dev,
6168892ea20SAggelos Economopoulos 			      "PCIe x%d Link, expect reduced performance\n",
6178892ea20SAggelos Economopoulos 			      sc->link_width);
6188892ea20SAggelos Economopoulos 		aligned = 1;
6198892ea20SAggelos Economopoulos 		goto abort;
6208892ea20SAggelos Economopoulos 	}
6218892ea20SAggelos Economopoulos 
6228892ea20SAggelos Economopoulos 	if (0 == mxge_firmware_probe(sc))
6238892ea20SAggelos Economopoulos 		return 0;
6248892ea20SAggelos Economopoulos 
6258892ea20SAggelos Economopoulos abort:
6268892ea20SAggelos Economopoulos 	if (aligned) {
6278892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
6288892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
6298892ea20SAggelos Economopoulos 	} else {
6308892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
6318892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
6328892ea20SAggelos Economopoulos 	}
6338892ea20SAggelos Economopoulos 	return (mxge_load_firmware(sc, 0));
6348892ea20SAggelos Economopoulos }
6358892ea20SAggelos Economopoulos 
6368892ea20SAggelos Economopoulos union qualhack
6378892ea20SAggelos Economopoulos {
6388892ea20SAggelos Economopoulos         const char *ro_char;
6398892ea20SAggelos Economopoulos         char *rw_char;
6408892ea20SAggelos Economopoulos };
6418892ea20SAggelos Economopoulos 
6428892ea20SAggelos Economopoulos static int
6438892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6448892ea20SAggelos Economopoulos {
6458892ea20SAggelos Economopoulos 
6468892ea20SAggelos Economopoulos 
6478892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6488892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
6498892ea20SAggelos Economopoulos 			      be32toh(hdr->mcp_type));
6508892ea20SAggelos Economopoulos 		return EIO;
6518892ea20SAggelos Economopoulos 	}
6528892ea20SAggelos Economopoulos 
6538892ea20SAggelos Economopoulos 	/* save firmware version for sysctl */
6548892ea20SAggelos Economopoulos 	strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version));
6558892ea20SAggelos Economopoulos 	if (mxge_verbose)
6568892ea20SAggelos Economopoulos 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
6578892ea20SAggelos Economopoulos 
658b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6598892ea20SAggelos Economopoulos 	       &sc->fw_ver_minor, &sc->fw_ver_tiny);
6608892ea20SAggelos Economopoulos 
6618892ea20SAggelos Economopoulos 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR
6628892ea20SAggelos Economopoulos 	      && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6638892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Found firmware version %s\n",
6648892ea20SAggelos Economopoulos 			      sc->fw_version);
6658892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Driver needs %d.%d\n",
6668892ea20SAggelos Economopoulos 			      MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6678892ea20SAggelos Economopoulos 		return EINVAL;
6688892ea20SAggelos Economopoulos 	}
6698892ea20SAggelos Economopoulos 	return 0;
6708892ea20SAggelos Economopoulos 
6718892ea20SAggelos Economopoulos }
6728892ea20SAggelos Economopoulos 
6738892ea20SAggelos Economopoulos static void *
6748892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6758892ea20SAggelos Economopoulos {
6768892ea20SAggelos Economopoulos         void *ptr;
6778892ea20SAggelos Economopoulos 
678d777b84fSAggelos Economopoulos         ptr = kmalloc(items * size, M_TEMP, M_NOWAIT);
6798892ea20SAggelos Economopoulos         return ptr;
6808892ea20SAggelos Economopoulos }
6818892ea20SAggelos Economopoulos 
6828892ea20SAggelos Economopoulos static void
6838892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6848892ea20SAggelos Economopoulos {
685d777b84fSAggelos Economopoulos         kfree(ptr, M_TEMP);
6868892ea20SAggelos Economopoulos }
6878892ea20SAggelos Economopoulos 
6888892ea20SAggelos Economopoulos 
6898892ea20SAggelos Economopoulos static int
6908892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6918892ea20SAggelos Economopoulos {
692e16aed9bSAggelos Economopoulos 	struct fw_image *fw;
6938892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6948892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6958892ea20SAggelos Economopoulos 	int status;
6968892ea20SAggelos Economopoulos 	unsigned int i;
6978892ea20SAggelos Economopoulos 	char dummy;
6988892ea20SAggelos Economopoulos 	size_t fw_len;
6998892ea20SAggelos Economopoulos 
700e16aed9bSAggelos Economopoulos 	fw = firmware_image_load(sc->fw_name, NULL);
7018892ea20SAggelos Economopoulos 	if (fw == NULL) {
7028892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Could not find firmware image %s\n",
7038892ea20SAggelos Economopoulos 			      sc->fw_name);
7048892ea20SAggelos Economopoulos 		return ENOENT;
7058892ea20SAggelos Economopoulos 	}
706e16aed9bSAggelos Economopoulos #if 0
7078892ea20SAggelos Economopoulos 	/* setup zlib and decompress f/w */
7088892ea20SAggelos Economopoulos 	bzero(&zs, sizeof (zs));
7098892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
7108892ea20SAggelos Economopoulos 	zs.zfree = z_free;
7118892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
7128892ea20SAggelos Economopoulos 	if (status != Z_OK) {
7138892ea20SAggelos Economopoulos 		status = EIO;
7148892ea20SAggelos Economopoulos 		goto abort_with_fw;
7158892ea20SAggelos Economopoulos 	}
7168892ea20SAggelos Economopoulos 
7178892ea20SAggelos Economopoulos 	/* the uncompressed size is stored as the firmware version,
7188892ea20SAggelos Economopoulos 	   which would otherwise go unused */
7198892ea20SAggelos Economopoulos 	fw_len = (size_t) fw->version;
720d777b84fSAggelos Economopoulos 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_NOWAIT);
7218892ea20SAggelos Economopoulos 	if (inflate_buffer == NULL)
7228892ea20SAggelos Economopoulos 		goto abort_with_zs;
7238892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
7248892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
7258892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
7268892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
7278892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
7288892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
7298892ea20SAggelos Economopoulos 		device_printf(sc->dev, "zlib %d\n", status);
7308892ea20SAggelos Economopoulos 		status = EIO;
7318892ea20SAggelos Economopoulos 		goto abort_with_buffer;
7328892ea20SAggelos Economopoulos 	}
733e16aed9bSAggelos Economopoulos #endif
7348892ea20SAggelos Economopoulos 	/* check id */
7358892ea20SAggelos Economopoulos 	hdr_offset = htobe32(*(const uint32_t *)
736e16aed9bSAggelos Economopoulos 			     (fw->fw_image + MCP_HEADER_PTR_OFFSET));
7378892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
7388892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Bad firmware file");
7398892ea20SAggelos Economopoulos 		status = EIO;
740e16aed9bSAggelos Economopoulos 		goto abort_with_fw;
7418892ea20SAggelos Economopoulos 	}
742e16aed9bSAggelos Economopoulos 	hdr = (const void*)(fw->fw_image + hdr_offset);
7438892ea20SAggelos Economopoulos 
7448892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
7458892ea20SAggelos Economopoulos 	if (status != 0)
746e16aed9bSAggelos Economopoulos 		goto abort_with_fw;
7478892ea20SAggelos Economopoulos 
7488892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
7498892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
7508892ea20SAggelos Economopoulos 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i,
751e16aed9bSAggelos Economopoulos 			      fw->fw_image + i,
7528892ea20SAggelos Economopoulos 			      min(256U, (unsigned)(fw_len - i)));
7538892ea20SAggelos Economopoulos 		wmb();
7548892ea20SAggelos Economopoulos 		dummy = *sc->sram;
7558892ea20SAggelos Economopoulos 		wmb();
7568892ea20SAggelos Economopoulos 	}
7578892ea20SAggelos Economopoulos 
7588892ea20SAggelos Economopoulos 	*limit = fw_len;
7598892ea20SAggelos Economopoulos 	status = 0;
760e16aed9bSAggelos Economopoulos #if 0
7618892ea20SAggelos Economopoulos abort_with_buffer:
762d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7638892ea20SAggelos Economopoulos abort_with_zs:
7648892ea20SAggelos Economopoulos 	inflateEnd(&zs);
765e16aed9bSAggelos Economopoulos #endif
7668892ea20SAggelos Economopoulos abort_with_fw:
767e16aed9bSAggelos Economopoulos 	firmware_image_unload(fw);
7688892ea20SAggelos Economopoulos 	return status;
7698892ea20SAggelos Economopoulos }
7708892ea20SAggelos Economopoulos 
7718892ea20SAggelos Economopoulos /*
7728892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7738892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7748892ea20SAggelos Economopoulos  */
7758892ea20SAggelos Economopoulos 
7768892ea20SAggelos Economopoulos static void
7778892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7788892ea20SAggelos Economopoulos {
7798892ea20SAggelos Economopoulos 	char buf_bytes[72];
7808892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7818892ea20SAggelos Economopoulos 	volatile char *submit;
7828892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7838892ea20SAggelos Economopoulos 	int i;
7848892ea20SAggelos Economopoulos 
7858892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7868892ea20SAggelos Economopoulos 
7878892ea20SAggelos Economopoulos 	/* clear confirmation addr */
7888892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7898892ea20SAggelos Economopoulos 	*confirm = 0;
7908892ea20SAggelos Economopoulos 	wmb();
7918892ea20SAggelos Economopoulos 
7928892ea20SAggelos Economopoulos 	/* send an rdma command to the PCIe engine, and wait for the
7938892ea20SAggelos Economopoulos 	   response in the confirmation address.  The firmware should
7948892ea20SAggelos Economopoulos 	   write a -1 there to indicate it is alive and well
7958892ea20SAggelos Economopoulos 	*/
7968892ea20SAggelos Economopoulos 
7978892ea20SAggelos Economopoulos 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
7988892ea20SAggelos Economopoulos 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
7998892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
8008892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
8018892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
8028892ea20SAggelos Economopoulos 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr);
8038892ea20SAggelos Economopoulos 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr);
8048892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
8058892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
8068892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);			/* enable? */
8078892ea20SAggelos Economopoulos 
8088892ea20SAggelos Economopoulos 
8098892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
8108892ea20SAggelos Economopoulos 
8118892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
8128892ea20SAggelos Economopoulos 	wmb();
8138892ea20SAggelos Economopoulos 	DELAY(1000);
8148892ea20SAggelos Economopoulos 	wmb();
8158892ea20SAggelos Economopoulos 	i = 0;
8168892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
8178892ea20SAggelos Economopoulos 		DELAY(1000);
8188892ea20SAggelos Economopoulos 		i++;
8198892ea20SAggelos Economopoulos 	}
8208892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
8218892ea20SAggelos Economopoulos 		device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)",
8228892ea20SAggelos Economopoulos 			      (enable ? "enable" : "disable"), confirm,
8238892ea20SAggelos Economopoulos 			      *confirm);
8248892ea20SAggelos Economopoulos 	}
8258892ea20SAggelos Economopoulos 	return;
8268892ea20SAggelos Economopoulos }
8278892ea20SAggelos Economopoulos 
8288892ea20SAggelos Economopoulos static int
8298892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
8308892ea20SAggelos Economopoulos {
8318892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
8328892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
8338892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
8348892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
8358892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
8368892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
8378892ea20SAggelos Economopoulos 
8388892ea20SAggelos Economopoulos 	/* ensure buf is aligned to 8 bytes */
8398892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
8408892ea20SAggelos Economopoulos 
8418892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
8428892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
8438892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
8448892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
8458892ea20SAggelos Economopoulos 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
8468892ea20SAggelos Economopoulos 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
8478892ea20SAggelos Economopoulos 
8488892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
8498892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
8502e8181d0SAggelos Economopoulos 
8512e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
8522e8181d0SAggelos Economopoulos 
8538892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
8548892ea20SAggelos Economopoulos 	wmb();
8558892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
8568892ea20SAggelos Economopoulos 
8578892ea20SAggelos Economopoulos 	/* wait up to 20ms */
8588892ea20SAggelos Economopoulos 	err = EAGAIN;
8598892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total <  20; sleep_total++) {
8608892ea20SAggelos Economopoulos 		bus_dmamap_sync(sc->cmd_dma.dmat,
8618892ea20SAggelos Economopoulos 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
8628892ea20SAggelos Economopoulos 		wmb();
8638892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
8648892ea20SAggelos Economopoulos 		case 0:
8658892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
8668892ea20SAggelos Economopoulos 			err = 0;
8678892ea20SAggelos Economopoulos 			break;
8688892ea20SAggelos Economopoulos 		case 0xffffffff:
8698892ea20SAggelos Economopoulos 			DELAY(1000);
8708892ea20SAggelos Economopoulos 			break;
8718892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8728892ea20SAggelos Economopoulos 			err = ENOSYS;
8738892ea20SAggelos Economopoulos 			break;
8748892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8758892ea20SAggelos Economopoulos 			err = E2BIG;
8768892ea20SAggelos Economopoulos 			break;
8778892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8788892ea20SAggelos Economopoulos 			err = EBUSY;
8798892ea20SAggelos Economopoulos 			break;
8808892ea20SAggelos Economopoulos 		default:
8818892ea20SAggelos Economopoulos 			device_printf(sc->dev,
8828892ea20SAggelos Economopoulos 				      "mxge: command %d "
8838892ea20SAggelos Economopoulos 				      "failed, result = %d\n",
8848892ea20SAggelos Economopoulos 				      cmd, be32toh(response->result));
8858892ea20SAggelos Economopoulos 			err = ENXIO;
8868892ea20SAggelos Economopoulos 			break;
8878892ea20SAggelos Economopoulos 		}
8888892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8898892ea20SAggelos Economopoulos 			break;
8908892ea20SAggelos Economopoulos 	}
8918892ea20SAggelos Economopoulos 	if (err == EAGAIN)
8928892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mxge: command %d timed out"
8938892ea20SAggelos Economopoulos 			      "result = %d\n",
8948892ea20SAggelos Economopoulos 			      cmd, be32toh(response->result));
8952e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
8968892ea20SAggelos Economopoulos 	return err;
8978892ea20SAggelos Economopoulos }
8988892ea20SAggelos Economopoulos 
8998892ea20SAggelos Economopoulos static int
9008892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
9018892ea20SAggelos Economopoulos {
9028892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
9038892ea20SAggelos Economopoulos 	const size_t bytes = sizeof (struct mcp_gen_header);
9048892ea20SAggelos Economopoulos 	size_t hdr_offset;
9058892ea20SAggelos Economopoulos 	int status;
9068892ea20SAggelos Economopoulos 
9078892ea20SAggelos Economopoulos 	/* find running firmware header */
9088892ea20SAggelos Economopoulos 	hdr_offset = htobe32(*(volatile uint32_t *)
9098892ea20SAggelos Economopoulos 			     (sc->sram + MCP_HEADER_PTR_OFFSET));
9108892ea20SAggelos Economopoulos 
9118892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
9128892ea20SAggelos Economopoulos 		device_printf(sc->dev,
9138892ea20SAggelos Economopoulos 			      "Running firmware has bad header offset (%d)\n",
9148892ea20SAggelos Economopoulos 			      (int)hdr_offset);
9158892ea20SAggelos Economopoulos 		return EIO;
9168892ea20SAggelos Economopoulos 	}
9178892ea20SAggelos Economopoulos 
9188892ea20SAggelos Economopoulos 	/* copy header of running firmware from SRAM to host memory to
9198892ea20SAggelos Economopoulos 	 * validate firmware */
920d777b84fSAggelos Economopoulos 	hdr = kmalloc(bytes, M_DEVBUF, M_NOWAIT);
9218892ea20SAggelos Economopoulos 	if (hdr == NULL) {
922d777b84fSAggelos Economopoulos 		device_printf(sc->dev, "could not kmalloc firmware hdr\n");
9238892ea20SAggelos Economopoulos 		return ENOMEM;
9248892ea20SAggelos Economopoulos 	}
9258892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
9268892ea20SAggelos Economopoulos 				rman_get_bushandle(sc->mem_res),
9278892ea20SAggelos Economopoulos 				hdr_offset, (char *)hdr, bytes);
9288892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
929d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
9308892ea20SAggelos Economopoulos 
9318892ea20SAggelos Economopoulos 	/*
9328892ea20SAggelos Economopoulos 	 * check to see if adopted firmware has bug where adopting
9338892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
9348892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
9358892ea20SAggelos Economopoulos 	 */
9368892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
9378892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
9388892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
9398892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
9408892ea20SAggelos Economopoulos 			      "working around rx filter bug\n",
9418892ea20SAggelos Economopoulos 			      sc->fw_ver_major, sc->fw_ver_minor,
9428892ea20SAggelos Economopoulos 			      sc->fw_ver_tiny);
9438892ea20SAggelos Economopoulos 	}
9448892ea20SAggelos Economopoulos 
9458892ea20SAggelos Economopoulos 	return status;
9468892ea20SAggelos Economopoulos }
9478892ea20SAggelos Economopoulos 
9488892ea20SAggelos Economopoulos 
9498892ea20SAggelos Economopoulos static int
9508892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
9518892ea20SAggelos Economopoulos {
9528892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
9538892ea20SAggelos Economopoulos 	volatile char *submit;
9548892ea20SAggelos Economopoulos 	char buf_bytes[72];
9558892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
9568892ea20SAggelos Economopoulos 	int status, i;
9578892ea20SAggelos Economopoulos 
9588892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
9598892ea20SAggelos Economopoulos 
9608892ea20SAggelos Economopoulos 	size = sc->sram_size;
9618892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
9628892ea20SAggelos Economopoulos 	if (status) {
9638892ea20SAggelos Economopoulos 		if (!adopt)
9648892ea20SAggelos Economopoulos 			return status;
9658892ea20SAggelos Economopoulos 		/* Try to use the currently running firmware, if
9668892ea20SAggelos Economopoulos 		   it is new enough */
9678892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
9688892ea20SAggelos Economopoulos 		if (status) {
9698892ea20SAggelos Economopoulos 			device_printf(sc->dev,
9708892ea20SAggelos Economopoulos 				      "failed to adopt running firmware\n");
9718892ea20SAggelos Economopoulos 			return status;
9728892ea20SAggelos Economopoulos 		}
9738892ea20SAggelos Economopoulos 		device_printf(sc->dev,
9748892ea20SAggelos Economopoulos 			      "Successfully adopted running firmware\n");
9758892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9768892ea20SAggelos Economopoulos 			device_printf(sc->dev,
9778892ea20SAggelos Economopoulos 				"Using firmware currently running on NIC"
9788892ea20SAggelos Economopoulos 				 ".  For optimal\n");
9798892ea20SAggelos Economopoulos 			device_printf(sc->dev,
9808892ea20SAggelos Economopoulos 				 "performance consider loading optimized "
9818892ea20SAggelos Economopoulos 				 "firmware\n");
9828892ea20SAggelos Economopoulos 		}
9838892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9848892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9858892ea20SAggelos Economopoulos 		return 0;
9868892ea20SAggelos Economopoulos 	}
9878892ea20SAggelos Economopoulos 	/* clear confirmation addr */
9888892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9898892ea20SAggelos Economopoulos 	*confirm = 0;
9908892ea20SAggelos Economopoulos 	wmb();
9918892ea20SAggelos Economopoulos 	/* send a reload command to the bootstrap MCP, and wait for the
9928892ea20SAggelos Economopoulos 	   response in the confirmation address.  The firmware should
9938892ea20SAggelos Economopoulos 	   write a -1 there to indicate it is alive and well
9948892ea20SAggelos Economopoulos 	*/
9958892ea20SAggelos Economopoulos 
9968892ea20SAggelos Economopoulos 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr);
9978892ea20SAggelos Economopoulos 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr);
9988892ea20SAggelos Economopoulos 
9998892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
10008892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
10018892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
10028892ea20SAggelos Economopoulos 
10038892ea20SAggelos Economopoulos 	/* FIX: All newest firmware should un-protect the bottom of
10048892ea20SAggelos Economopoulos 	   the sram before handoff. However, the very first interfaces
10058892ea20SAggelos Economopoulos 	   do not. Therefore the handoff copy must skip the first 8 bytes
10068892ea20SAggelos Economopoulos 	*/
10078892ea20SAggelos Economopoulos 					/* where the code starts*/
10088892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
10098892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
10108892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
10118892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
10128892ea20SAggelos Economopoulos 
10138892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
10148892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
10158892ea20SAggelos Economopoulos 	wmb();
10168892ea20SAggelos Economopoulos 	DELAY(1000);
10178892ea20SAggelos Economopoulos 	wmb();
10188892ea20SAggelos Economopoulos 	i = 0;
10198892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
10208892ea20SAggelos Economopoulos 		DELAY(1000*10);
10218892ea20SAggelos Economopoulos 		i++;
10228892ea20SAggelos Economopoulos 		bus_dmamap_sync(sc->cmd_dma.dmat,
10238892ea20SAggelos Economopoulos 				sc->cmd_dma.map, BUS_DMASYNC_POSTREAD);
10248892ea20SAggelos Economopoulos 	}
10258892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
10268892ea20SAggelos Economopoulos 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
10278892ea20SAggelos Economopoulos 			confirm, *confirm);
10288892ea20SAggelos Economopoulos 
10298892ea20SAggelos Economopoulos 		return ENXIO;
10308892ea20SAggelos Economopoulos 	}
10318892ea20SAggelos Economopoulos 	return 0;
10328892ea20SAggelos Economopoulos }
10338892ea20SAggelos Economopoulos 
10348892ea20SAggelos Economopoulos static int
10358892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
10368892ea20SAggelos Economopoulos {
10378892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10388892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
10398892ea20SAggelos Economopoulos 	int status;
10408892ea20SAggelos Economopoulos 
10418892ea20SAggelos Economopoulos 
10428892ea20SAggelos Economopoulos 	cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
10438892ea20SAggelos Economopoulos 		     | (addr[2] << 8) | addr[3]);
10448892ea20SAggelos Economopoulos 
10458892ea20SAggelos Economopoulos 	cmd.data1 = ((addr[4] << 8) | (addr[5]));
10468892ea20SAggelos Economopoulos 
10478892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
10488892ea20SAggelos Economopoulos 	return status;
10498892ea20SAggelos Economopoulos }
10508892ea20SAggelos Economopoulos 
10518892ea20SAggelos Economopoulos static int
10528892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
10538892ea20SAggelos Economopoulos {
10548892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10558892ea20SAggelos Economopoulos 	int status;
10568892ea20SAggelos Economopoulos 
10578892ea20SAggelos Economopoulos 	if (pause)
10588892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL,
10598892ea20SAggelos Economopoulos 				       &cmd);
10608892ea20SAggelos Economopoulos 	else
10618892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL,
10628892ea20SAggelos Economopoulos 				       &cmd);
10638892ea20SAggelos Economopoulos 
10648892ea20SAggelos Economopoulos 	if (status) {
10658892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed to set flow control mode\n");
10668892ea20SAggelos Economopoulos 		return ENXIO;
10678892ea20SAggelos Economopoulos 	}
10688892ea20SAggelos Economopoulos 	sc->pause = pause;
10698892ea20SAggelos Economopoulos 	return 0;
10708892ea20SAggelos Economopoulos }
10718892ea20SAggelos Economopoulos 
10728892ea20SAggelos Economopoulos static void
10738892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
10748892ea20SAggelos Economopoulos {
10758892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10768892ea20SAggelos Economopoulos 	int status;
10778892ea20SAggelos Economopoulos 
10788892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10798892ea20SAggelos Economopoulos 		promisc = 1;
10808892ea20SAggelos Economopoulos 
10818892ea20SAggelos Economopoulos 	if (promisc)
10828892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC,
10838892ea20SAggelos Economopoulos 				       &cmd);
10848892ea20SAggelos Economopoulos 	else
10858892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC,
10868892ea20SAggelos Economopoulos 				       &cmd);
10878892ea20SAggelos Economopoulos 
10888892ea20SAggelos Economopoulos 	if (status) {
10898892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed to set promisc mode\n");
10908892ea20SAggelos Economopoulos 	}
10918892ea20SAggelos Economopoulos }
10928892ea20SAggelos Economopoulos 
10938892ea20SAggelos Economopoulos static void
10948892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10958892ea20SAggelos Economopoulos {
10968892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10978892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10988892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10998892ea20SAggelos Economopoulos 	int err;
11008892ea20SAggelos Economopoulos 
11018892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
11028892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
11038892ea20SAggelos Economopoulos 		return;
11048892ea20SAggelos Economopoulos 
11058892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
11068892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
11078892ea20SAggelos Economopoulos 	if (err != 0) {
11088892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
11098892ea20SAggelos Economopoulos 		       " error status: %d\n", err);
11108892ea20SAggelos Economopoulos 		return;
11118892ea20SAggelos Economopoulos 	}
11128892ea20SAggelos Economopoulos 
11138892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
11148892ea20SAggelos Economopoulos 		return;
11158892ea20SAggelos Economopoulos 
11168892ea20SAggelos Economopoulos 	if (ifp->if_flags & IFF_ALLMULTI)
11178892ea20SAggelos Economopoulos 		/* request to disable multicast filtering, so quit here */
11188892ea20SAggelos Economopoulos 		return;
11198892ea20SAggelos Economopoulos 
11208892ea20SAggelos Economopoulos 	/* Flush all the filters */
11218892ea20SAggelos Economopoulos 
11228892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
11238892ea20SAggelos Economopoulos 	if (err != 0) {
11248892ea20SAggelos Economopoulos 		device_printf(sc->dev,
11258892ea20SAggelos Economopoulos 			      "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS"
11268892ea20SAggelos Economopoulos 			      ", error status: %d\n", err);
11278892ea20SAggelos Economopoulos 		return;
11288892ea20SAggelos Economopoulos 	}
11298892ea20SAggelos Economopoulos 
11308892ea20SAggelos Economopoulos 	/* Walk the multicast list, and add each address */
11318892ea20SAggelos Economopoulos 
11322e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(ifp->if_serializer);
1133b915556eSAggelos Economopoulos 	LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
11348892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
11358892ea20SAggelos Economopoulos 			continue;
11368892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
11378892ea20SAggelos Economopoulos 		      &cmd.data0, 4);
11388892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
11398892ea20SAggelos Economopoulos 		      &cmd.data1, 2);
11408892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
11418892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
11428892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
11438892ea20SAggelos Economopoulos 		if (err != 0) {
11448892ea20SAggelos Economopoulos 			device_printf(sc->dev, "Failed "
11458892ea20SAggelos Economopoulos 			       "MXGEFW_JOIN_MULTICAST_GROUP, error status:"
11468892ea20SAggelos Economopoulos 			       "%d\t", err);
11478892ea20SAggelos Economopoulos 			/* abort, leaving multicast filtering off */
11482e8181d0SAggelos Economopoulos 			lwkt_serialize_exit(ifp->if_serializer);
11498892ea20SAggelos Economopoulos 			return;
11508892ea20SAggelos Economopoulos 		}
11518892ea20SAggelos Economopoulos 	}
11522e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(ifp->if_serializer);
11538892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
11548892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
11558892ea20SAggelos Economopoulos 	if (err != 0) {
11568892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI"
11578892ea20SAggelos Economopoulos 		       ", error status: %d\n", err);
11588892ea20SAggelos Economopoulos 	}
11598892ea20SAggelos Economopoulos }
11608892ea20SAggelos Economopoulos 
11618892ea20SAggelos Economopoulos static int
11628892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
11638892ea20SAggelos Economopoulos {
11648892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11658892ea20SAggelos Economopoulos 	int status;
11668892ea20SAggelos Economopoulos 
11678892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
11688892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11698892ea20SAggelos Economopoulos 
11708892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
11718892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
11728892ea20SAggelos Economopoulos 	cmd.data0 = 0;
11738892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
11748892ea20SAggelos Economopoulos 			       &cmd);
11758892ea20SAggelos Economopoulos 	if (status == 0)
11768892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11778892ea20SAggelos Economopoulos 
11788892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
11798892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
11808892ea20SAggelos Economopoulos }
11818892ea20SAggelos Economopoulos 
11828892ea20SAggelos Economopoulos static int
11838892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11848892ea20SAggelos Economopoulos {
11858892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11868892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11878892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11888892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11898892ea20SAggelos Economopoulos 	int slice, status;
11908892ea20SAggelos Economopoulos 
11918892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
11928892ea20SAggelos Economopoulos 	   is alive */
11938892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11948892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11958892ea20SAggelos Economopoulos 	if (status != 0) {
11968892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
11978892ea20SAggelos Economopoulos 		return ENXIO;
11988892ea20SAggelos Economopoulos 	}
11998892ea20SAggelos Economopoulos 
12008892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
12018892ea20SAggelos Economopoulos 
12028892ea20SAggelos Economopoulos 
12038892ea20SAggelos Economopoulos 	/* set the intrq size */
12048892ea20SAggelos Economopoulos 	cmd.data0 = sc->rx_ring_size;
12058892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
12068892ea20SAggelos Economopoulos 
12078892ea20SAggelos Economopoulos 	/*
12088892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
12098892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
12108892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
12118892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
12128892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
12138892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
12148892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
12158892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
12168892ea20SAggelos Economopoulos 	 */
12178892ea20SAggelos Economopoulos 
12188892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
12198892ea20SAggelos Economopoulos 		/* ask the maximum number of slices it supports */
12208892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
12218892ea20SAggelos Economopoulos 					   &cmd);
12228892ea20SAggelos Economopoulos 		if (status != 0) {
12238892ea20SAggelos Economopoulos 			device_printf(sc->dev,
12248892ea20SAggelos Economopoulos 				      "failed to get number of slices\n");
12258892ea20SAggelos Economopoulos 			return status;
12268892ea20SAggelos Economopoulos 		}
12278892ea20SAggelos Economopoulos 		/*
12288892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
12298892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
12308892ea20SAggelos Economopoulos 		 */
12318892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
12328892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
12338892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
12348892ea20SAggelos Economopoulos 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
12358892ea20SAggelos Economopoulos #endif
12368892ea20SAggelos Economopoulos 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES,
12378892ea20SAggelos Economopoulos 					   &cmd);
12388892ea20SAggelos Economopoulos 		if (status != 0) {
12398892ea20SAggelos Economopoulos 			device_printf(sc->dev,
12408892ea20SAggelos Economopoulos 				      "failed to set number of slices\n");
12418892ea20SAggelos Economopoulos 			return status;
12428892ea20SAggelos Economopoulos 		}
12438892ea20SAggelos Economopoulos 	}
12448892ea20SAggelos Economopoulos 
12458892ea20SAggelos Economopoulos 
12468892ea20SAggelos Economopoulos 	if (interrupts_setup) {
12478892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
12488892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
12498892ea20SAggelos Economopoulos 			rx_done = &sc->ss[slice].rx_done;
12508892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
12518892ea20SAggelos Economopoulos 			cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr);
12528892ea20SAggelos Economopoulos 			cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr);
12538892ea20SAggelos Economopoulos 			cmd.data2 = slice;
12548892ea20SAggelos Economopoulos 			status |= mxge_send_cmd(sc,
12558892ea20SAggelos Economopoulos 						MXGEFW_CMD_SET_INTRQ_DMA,
12568892ea20SAggelos Economopoulos 						&cmd);
12578892ea20SAggelos Economopoulos 		}
12588892ea20SAggelos Economopoulos 	}
12598892ea20SAggelos Economopoulos 
12608892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc,
12618892ea20SAggelos Economopoulos 				MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
12628892ea20SAggelos Economopoulos 
12638892ea20SAggelos Economopoulos 
12648892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12658892ea20SAggelos Economopoulos 
12668892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12678892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12688892ea20SAggelos Economopoulos 
12698892ea20SAggelos Economopoulos 
12708892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
12718892ea20SAggelos Economopoulos 				&cmd);
12728892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
12738892ea20SAggelos Economopoulos 	if (status != 0) {
12748892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed set interrupt parameters\n");
12758892ea20SAggelos Economopoulos 		return status;
12768892ea20SAggelos Economopoulos 	}
12778892ea20SAggelos Economopoulos 
12788892ea20SAggelos Economopoulos 
12798892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12808892ea20SAggelos Economopoulos 
12818892ea20SAggelos Economopoulos 
12828892ea20SAggelos Economopoulos 	/* run a DMA benchmark */
12838892ea20SAggelos Economopoulos 	(void) mxge_dma_test(sc, MXGEFW_DMA_TEST);
12848892ea20SAggelos Economopoulos 
12858892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
12868892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
12878892ea20SAggelos Economopoulos 
12888892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
12898892ea20SAggelos Economopoulos 		/* reset mcp/driver shared state back to 0 */
12908892ea20SAggelos Economopoulos 		ss->rx_done.idx = 0;
12918892ea20SAggelos Economopoulos 		ss->rx_done.cnt = 0;
12928892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12938892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12948892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12958892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12968892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12978892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12988892ea20SAggelos Economopoulos 		ss->tx.wake = 0;
12998892ea20SAggelos Economopoulos 		ss->tx.defrag = 0;
13008892ea20SAggelos Economopoulos 		ss->tx.stall = 0;
13018892ea20SAggelos Economopoulos 		ss->rx_big.cnt = 0;
13028892ea20SAggelos Economopoulos 		ss->rx_small.cnt = 0;
13038892ea20SAggelos Economopoulos 		ss->lro_bad_csum = 0;
13048892ea20SAggelos Economopoulos 		ss->lro_queued = 0;
13058892ea20SAggelos Economopoulos 		ss->lro_flushed = 0;
13068892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
13078892ea20SAggelos Economopoulos 			ss->fw_stats->valid = 0;
13088892ea20SAggelos Economopoulos 			ss->fw_stats->send_done_count = 0;
13098892ea20SAggelos Economopoulos 		}
13108892ea20SAggelos Economopoulos 	}
13118892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
13128892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
13138892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
13148892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
13158892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
13168892ea20SAggelos Economopoulos 	return status;
13178892ea20SAggelos Economopoulos }
13188892ea20SAggelos Economopoulos 
13198892ea20SAggelos Economopoulos static int
13208892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
13218892ea20SAggelos Economopoulos {
13228892ea20SAggelos Economopoulos         mxge_softc_t *sc;
13238892ea20SAggelos Economopoulos         unsigned int intr_coal_delay;
13248892ea20SAggelos Economopoulos         int err;
13258892ea20SAggelos Economopoulos 
13268892ea20SAggelos Economopoulos         sc = arg1;
13278892ea20SAggelos Economopoulos         intr_coal_delay = sc->intr_coal_delay;
13288892ea20SAggelos Economopoulos         err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
13298892ea20SAggelos Economopoulos         if (err != 0) {
13308892ea20SAggelos Economopoulos                 return err;
13318892ea20SAggelos Economopoulos         }
13328892ea20SAggelos Economopoulos         if (intr_coal_delay == sc->intr_coal_delay)
13338892ea20SAggelos Economopoulos                 return 0;
13348892ea20SAggelos Economopoulos 
13358892ea20SAggelos Economopoulos         if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
13368892ea20SAggelos Economopoulos                 return EINVAL;
13378892ea20SAggelos Economopoulos 
13382e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
13398892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
13408892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
13418892ea20SAggelos Economopoulos 
13422e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
13438892ea20SAggelos Economopoulos         return err;
13448892ea20SAggelos Economopoulos }
13458892ea20SAggelos Economopoulos 
13468892ea20SAggelos Economopoulos static int
13478892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
13488892ea20SAggelos Economopoulos {
13498892ea20SAggelos Economopoulos         mxge_softc_t *sc;
13508892ea20SAggelos Economopoulos         unsigned int enabled;
13518892ea20SAggelos Economopoulos         int err;
13528892ea20SAggelos Economopoulos 
13538892ea20SAggelos Economopoulos         sc = arg1;
13548892ea20SAggelos Economopoulos         enabled = sc->pause;
13558892ea20SAggelos Economopoulos         err = sysctl_handle_int(oidp, &enabled, arg2, req);
13568892ea20SAggelos Economopoulos         if (err != 0) {
13578892ea20SAggelos Economopoulos                 return err;
13588892ea20SAggelos Economopoulos         }
13598892ea20SAggelos Economopoulos         if (enabled == sc->pause)
13608892ea20SAggelos Economopoulos                 return 0;
13618892ea20SAggelos Economopoulos 
13622e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
13638892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
13642e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
13658892ea20SAggelos Economopoulos         return err;
13668892ea20SAggelos Economopoulos }
13678892ea20SAggelos Economopoulos 
13688892ea20SAggelos Economopoulos static int
13698892ea20SAggelos Economopoulos mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt)
13708892ea20SAggelos Economopoulos {
13718892ea20SAggelos Economopoulos 	struct ifnet *ifp;
13728892ea20SAggelos Economopoulos 	int err = 0;
13738892ea20SAggelos Economopoulos 
13748892ea20SAggelos Economopoulos 	ifp = sc->ifp;
13758892ea20SAggelos Economopoulos 	if (lro_cnt == 0)
13768892ea20SAggelos Economopoulos 		ifp->if_capenable &= ~IFCAP_LRO;
13778892ea20SAggelos Economopoulos 	else
13788892ea20SAggelos Economopoulos 		ifp->if_capenable |= IFCAP_LRO;
13798892ea20SAggelos Economopoulos 	sc->lro_cnt = lro_cnt;
13802ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
13818892ea20SAggelos Economopoulos 		mxge_close(sc);
13828892ea20SAggelos Economopoulos 		err = mxge_open(sc);
13838892ea20SAggelos Economopoulos 	}
13848892ea20SAggelos Economopoulos 	return err;
13858892ea20SAggelos Economopoulos }
13868892ea20SAggelos Economopoulos 
13878892ea20SAggelos Economopoulos static int
13888892ea20SAggelos Economopoulos mxge_change_lro(SYSCTL_HANDLER_ARGS)
13898892ea20SAggelos Economopoulos {
13908892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
13918892ea20SAggelos Economopoulos 	unsigned int lro_cnt;
13928892ea20SAggelos Economopoulos 	int err;
13938892ea20SAggelos Economopoulos 
13948892ea20SAggelos Economopoulos 	sc = arg1;
13958892ea20SAggelos Economopoulos 	lro_cnt = sc->lro_cnt;
13968892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &lro_cnt, arg2, req);
13978892ea20SAggelos Economopoulos 	if (err != 0)
13988892ea20SAggelos Economopoulos 		return err;
13998892ea20SAggelos Economopoulos 
14008892ea20SAggelos Economopoulos 	if (lro_cnt == sc->lro_cnt)
14018892ea20SAggelos Economopoulos 		return 0;
14028892ea20SAggelos Economopoulos 
14038892ea20SAggelos Economopoulos 	if (lro_cnt > 128)
14048892ea20SAggelos Economopoulos 		return EINVAL;
14058892ea20SAggelos Economopoulos 
14062e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
14078892ea20SAggelos Economopoulos 	err = mxge_change_lro_locked(sc, lro_cnt);
14082e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
14098892ea20SAggelos Economopoulos 	return err;
14108892ea20SAggelos Economopoulos }
14118892ea20SAggelos Economopoulos 
14128892ea20SAggelos Economopoulos static int
14138892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
14148892ea20SAggelos Economopoulos {
14158892ea20SAggelos Economopoulos         int err;
14168892ea20SAggelos Economopoulos 
14178892ea20SAggelos Economopoulos         if (arg1 == NULL)
14188892ea20SAggelos Economopoulos                 return EFAULT;
14198892ea20SAggelos Economopoulos         arg2 = be32toh(*(int *)arg1);
14208892ea20SAggelos Economopoulos         arg1 = NULL;
14218892ea20SAggelos Economopoulos         err = sysctl_handle_int(oidp, arg1, arg2, req);
14228892ea20SAggelos Economopoulos 
14238892ea20SAggelos Economopoulos         return err;
14248892ea20SAggelos Economopoulos }
14258892ea20SAggelos Economopoulos 
14268892ea20SAggelos Economopoulos static void
14278892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
14288892ea20SAggelos Economopoulos {
14298892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
14308892ea20SAggelos Economopoulos 	int slice;
14318892ea20SAggelos Economopoulos 
1432e7f7e6deSAggelos Economopoulos 	if (sc->sysctl_tree != NULL) {
1433e7f7e6deSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1434e7f7e6deSAggelos Economopoulos 		sc->sysctl_tree = NULL;
1435e7f7e6deSAggelos Economopoulos 	}
14368892ea20SAggelos Economopoulos 	if (sc->slice_sysctl_tree == NULL)
14378892ea20SAggelos Economopoulos 		return;
14388892ea20SAggelos Economopoulos 
14398892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
14408892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
14418892ea20SAggelos Economopoulos 		if (ss == NULL || ss->sysctl_tree == NULL)
14428892ea20SAggelos Economopoulos 			continue;
14438892ea20SAggelos Economopoulos 		sysctl_ctx_free(&ss->sysctl_ctx);
14448892ea20SAggelos Economopoulos 		ss->sysctl_tree = NULL;
14458892ea20SAggelos Economopoulos 	}
14468892ea20SAggelos Economopoulos 	sysctl_ctx_free(&sc->slice_sysctl_ctx);
14478892ea20SAggelos Economopoulos 	sc->slice_sysctl_tree = NULL;
14488892ea20SAggelos Economopoulos }
14498892ea20SAggelos Economopoulos 
14508892ea20SAggelos Economopoulos static void
14518892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
14528892ea20SAggelos Economopoulos {
14538892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
14548892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
14558892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
14568892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
14578892ea20SAggelos Economopoulos 	int slice;
14588892ea20SAggelos Economopoulos 	char slice_num[8];
14598892ea20SAggelos Economopoulos 
1460b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1461b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1462b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
1463b6737651SAggelos Economopoulos 					  OID_AUTO,
1464b6737651SAggelos Economopoulos 					  device_get_nameunit(sc->dev),
1465b6737651SAggelos Economopoulos 					  CTLFLAG_RD, 0, "");
1466b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1467b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1468b6737651SAggelos Economopoulos 		return;
1469b6737651SAggelos Economopoulos 	}
1470b6737651SAggelos Economopoulos 
1471b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
14728892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
14738892ea20SAggelos Economopoulos 
14748892ea20SAggelos Economopoulos 	/* random information */
14758892ea20SAggelos Economopoulos 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14768892ea20SAggelos Economopoulos 		       "firmware_version",
14778892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->fw_version,
14788892ea20SAggelos Economopoulos 		       0, "firmware version");
14798892ea20SAggelos Economopoulos 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14808892ea20SAggelos Economopoulos 		       "serial_number",
14818892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->serial_number_string,
14828892ea20SAggelos Economopoulos 		       0, "serial number");
14838892ea20SAggelos Economopoulos 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO,
14848892ea20SAggelos Economopoulos 		       "product_code",
14858892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->product_code_string,
14868892ea20SAggelos Economopoulos 		       0, "product_code");
14878892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14888892ea20SAggelos Economopoulos 		       "pcie_link_width",
14898892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->link_width,
14908892ea20SAggelos Economopoulos 		       0, "tx_boundary");
14918892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14928892ea20SAggelos Economopoulos 		       "tx_boundary",
14938892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->tx_boundary,
14948892ea20SAggelos Economopoulos 		       0, "tx_boundary");
14958892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
14968892ea20SAggelos Economopoulos 		       "write_combine",
14978892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->wc,
14988892ea20SAggelos Economopoulos 		       0, "write combining PIO?");
14998892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15008892ea20SAggelos Economopoulos 		       "read_dma_MBs",
15018892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->read_dma,
15028892ea20SAggelos Economopoulos 		       0, "DMA Read speed in MB/s");
15038892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15048892ea20SAggelos Economopoulos 		       "write_dma_MBs",
15058892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->write_dma,
15068892ea20SAggelos Economopoulos 		       0, "DMA Write speed in MB/s");
15078892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15088892ea20SAggelos Economopoulos 		       "read_write_dma_MBs",
15098892ea20SAggelos Economopoulos 		       CTLFLAG_RD, &sc->read_write_dma,
15108892ea20SAggelos Economopoulos 		       0, "DMA concurrent Read/Write speed in MB/s");
15118892ea20SAggelos Economopoulos 
15128892ea20SAggelos Economopoulos 
15138892ea20SAggelos Economopoulos 	/* performance related tunables */
15148892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15158892ea20SAggelos Economopoulos 			"intr_coal_delay",
15168892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RW, sc,
15178892ea20SAggelos Economopoulos 			0, mxge_change_intr_coal,
15188892ea20SAggelos Economopoulos 			"I", "interrupt coalescing delay in usecs");
15198892ea20SAggelos Economopoulos 
15208892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15218892ea20SAggelos Economopoulos 			"flow_control_enabled",
15228892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RW, sc,
15238892ea20SAggelos Economopoulos 			0, mxge_change_flow_control,
15248892ea20SAggelos Economopoulos 			"I", "interrupt coalescing delay in usecs");
15258892ea20SAggelos Economopoulos 
15268892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
15278892ea20SAggelos Economopoulos 		       "deassert_wait",
15288892ea20SAggelos Economopoulos 		       CTLFLAG_RW, &mxge_deassert_wait,
15298892ea20SAggelos Economopoulos 		       0, "Wait for IRQ line to go low in ihandler");
15308892ea20SAggelos Economopoulos 
15318892ea20SAggelos Economopoulos 	/* stats block from firmware is in network byte order.
15328892ea20SAggelos Economopoulos 	   Need to swap it */
15338892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15348892ea20SAggelos Economopoulos 			"link_up",
15358892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->link_up,
15368892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15378892ea20SAggelos Economopoulos 			"I", "link up");
15388892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15398892ea20SAggelos Economopoulos 			"rdma_tags_available",
15408892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available,
15418892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15428892ea20SAggelos Economopoulos 			"I", "rdma_tags_available");
15438892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15448892ea20SAggelos Economopoulos 			"dropped_bad_crc32",
15458892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15468892ea20SAggelos Economopoulos 			&fw->dropped_bad_crc32,
15478892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15488892ea20SAggelos Economopoulos 			"I", "dropped_bad_crc32");
15498892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15508892ea20SAggelos Economopoulos 			"dropped_bad_phy",
15518892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15528892ea20SAggelos Economopoulos 			&fw->dropped_bad_phy,
15538892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15548892ea20SAggelos Economopoulos 			"I", "dropped_bad_phy");
15558892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15568892ea20SAggelos Economopoulos 			"dropped_link_error_or_filtered",
15578892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15588892ea20SAggelos Economopoulos 			&fw->dropped_link_error_or_filtered,
15598892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15608892ea20SAggelos Economopoulos 			"I", "dropped_link_error_or_filtered");
15618892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15628892ea20SAggelos Economopoulos 			"dropped_link_overflow",
15638892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow,
15648892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15658892ea20SAggelos Economopoulos 			"I", "dropped_link_overflow");
15668892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15678892ea20SAggelos Economopoulos 			"dropped_multicast_filtered",
15688892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15698892ea20SAggelos Economopoulos 			&fw->dropped_multicast_filtered,
15708892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15718892ea20SAggelos Economopoulos 			"I", "dropped_multicast_filtered");
15728892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15738892ea20SAggelos Economopoulos 			"dropped_no_big_buffer",
15748892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer,
15758892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15768892ea20SAggelos Economopoulos 			"I", "dropped_no_big_buffer");
15778892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15788892ea20SAggelos Economopoulos 			"dropped_no_small_buffer",
15798892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15808892ea20SAggelos Economopoulos 			&fw->dropped_no_small_buffer,
15818892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15828892ea20SAggelos Economopoulos 			"I", "dropped_no_small_buffer");
15838892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15848892ea20SAggelos Economopoulos 			"dropped_overrun",
15858892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun,
15868892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15878892ea20SAggelos Economopoulos 			"I", "dropped_overrun");
15888892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15898892ea20SAggelos Economopoulos 			"dropped_pause",
15908892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD,
15918892ea20SAggelos Economopoulos 			&fw->dropped_pause,
15928892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15938892ea20SAggelos Economopoulos 			"I", "dropped_pause");
15948892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
15958892ea20SAggelos Economopoulos 			"dropped_runt",
15968892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt,
15978892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
15988892ea20SAggelos Economopoulos 			"I", "dropped_runt");
15998892ea20SAggelos Economopoulos 
16008892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
16018892ea20SAggelos Economopoulos 			"dropped_unicast_filtered",
16028892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered,
16038892ea20SAggelos Economopoulos 			0, mxge_handle_be32,
16048892ea20SAggelos Economopoulos 			"I", "dropped_unicast_filtered");
16058892ea20SAggelos Economopoulos 
16068892ea20SAggelos Economopoulos 	/* verbose printing? */
16078892ea20SAggelos Economopoulos 	SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16088892ea20SAggelos Economopoulos 		       "verbose",
16098892ea20SAggelos Economopoulos 		       CTLFLAG_RW, &mxge_verbose,
16108892ea20SAggelos Economopoulos 		       0, "verbose printing");
16118892ea20SAggelos Economopoulos 
16128892ea20SAggelos Economopoulos 	/* lro */
16138892ea20SAggelos Economopoulos 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO,
16148892ea20SAggelos Economopoulos 			"lro_cnt",
16158892ea20SAggelos Economopoulos 			CTLTYPE_INT|CTLFLAG_RW, sc,
16168892ea20SAggelos Economopoulos 			0, mxge_change_lro,
16178892ea20SAggelos Economopoulos 			"I", "number of lro merge queues");
16188892ea20SAggelos Economopoulos 
16198892ea20SAggelos Economopoulos 
16208892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
16218892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
16228892ea20SAggelos Economopoulos 	sc->slice_sysctl_tree =
16238892ea20SAggelos Economopoulos 		SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO,
16248892ea20SAggelos Economopoulos 				"slice", CTLFLAG_RD, 0, "");
16258892ea20SAggelos Economopoulos 
16268892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
16278892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
16288892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
16298892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
16308892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1631b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
16328892ea20SAggelos Economopoulos 		ss->sysctl_tree =
16338892ea20SAggelos Economopoulos 			SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num,
16348892ea20SAggelos Economopoulos 					CTLFLAG_RD, 0, "");
16358892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
16368892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16378892ea20SAggelos Economopoulos 			       "rx_small_cnt",
16388892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->rx_small.cnt,
16398892ea20SAggelos Economopoulos 			       0, "rx_small_cnt");
16408892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16418892ea20SAggelos Economopoulos 			       "rx_big_cnt",
16428892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->rx_big.cnt,
16438892ea20SAggelos Economopoulos 			       0, "rx_small_cnt");
16448892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16458892ea20SAggelos Economopoulos 			       "lro_flushed", CTLFLAG_RD, &ss->lro_flushed,
16468892ea20SAggelos Economopoulos 			       0, "number of lro merge queues flushed");
16478892ea20SAggelos Economopoulos 
16488892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16498892ea20SAggelos Economopoulos 			       "lro_queued", CTLFLAG_RD, &ss->lro_queued,
16508892ea20SAggelos Economopoulos 			       0, "number of frames appended to lro merge"
16518892ea20SAggelos Economopoulos 			       "queues");
16528892ea20SAggelos Economopoulos 
16538892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
16548892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
16558892ea20SAggelos Economopoulos 		if (slice > 0)
16568892ea20SAggelos Economopoulos 			continue;
16578892ea20SAggelos Economopoulos #endif
16588892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16598892ea20SAggelos Economopoulos 			       "tx_req",
16608892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.req,
16618892ea20SAggelos Economopoulos 			       0, "tx_req");
16628892ea20SAggelos Economopoulos 
16638892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16648892ea20SAggelos Economopoulos 			       "tx_done",
16658892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.done,
16668892ea20SAggelos Economopoulos 			       0, "tx_done");
16678892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16688892ea20SAggelos Economopoulos 			       "tx_pkt_done",
16698892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.pkt_done,
16708892ea20SAggelos Economopoulos 			       0, "tx_done");
16718892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16728892ea20SAggelos Economopoulos 			       "tx_stall",
16738892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.stall,
16748892ea20SAggelos Economopoulos 			       0, "tx_stall");
16758892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16768892ea20SAggelos Economopoulos 			       "tx_wake",
16778892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.wake,
16788892ea20SAggelos Economopoulos 			       0, "tx_wake");
16798892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16808892ea20SAggelos Economopoulos 			       "tx_defrag",
16818892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.defrag,
16828892ea20SAggelos Economopoulos 			       0, "tx_defrag");
16838892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16848892ea20SAggelos Economopoulos 			       "tx_queue_active",
16858892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.queue_active,
16868892ea20SAggelos Economopoulos 			       0, "tx_queue_active");
16878892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16888892ea20SAggelos Economopoulos 			       "tx_activate",
16898892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.activate,
16908892ea20SAggelos Economopoulos 			       0, "tx_activate");
16918892ea20SAggelos Economopoulos 		SYSCTL_ADD_INT(ctx, children, OID_AUTO,
16928892ea20SAggelos Economopoulos 			       "tx_deactivate",
16938892ea20SAggelos Economopoulos 			       CTLFLAG_RD, &ss->tx.deactivate,
16948892ea20SAggelos Economopoulos 			       0, "tx_deactivate");
16958892ea20SAggelos Economopoulos 	}
16968892ea20SAggelos Economopoulos }
16978892ea20SAggelos Economopoulos 
16988892ea20SAggelos Economopoulos /* copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
16998892ea20SAggelos Economopoulos    backwards one at a time and handle ring wraps */
17008892ea20SAggelos Economopoulos 
17018892ea20SAggelos Economopoulos static inline void
17028892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
17038892ea20SAggelos Economopoulos 			    mcp_kreq_ether_send_t *src, int cnt)
17048892ea20SAggelos Economopoulos {
17058892ea20SAggelos Economopoulos         int idx, starting_slot;
17068892ea20SAggelos Economopoulos         starting_slot = tx->req;
17078892ea20SAggelos Economopoulos         while (cnt > 1) {
17088892ea20SAggelos Economopoulos                 cnt--;
17098892ea20SAggelos Economopoulos                 idx = (starting_slot + cnt) & tx->mask;
17108892ea20SAggelos Economopoulos                 mxge_pio_copy(&tx->lanai[idx],
17118892ea20SAggelos Economopoulos 			      &src[cnt], sizeof(*src));
17128892ea20SAggelos Economopoulos                 wmb();
17138892ea20SAggelos Economopoulos         }
17148892ea20SAggelos Economopoulos }
17158892ea20SAggelos Economopoulos 
17168892ea20SAggelos Economopoulos /*
17178892ea20SAggelos Economopoulos  * copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
17188892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
17198892ea20SAggelos Economopoulos  * pio handler in the nic.   We re-write the first segment's flags
17208892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
17218892ea20SAggelos Economopoulos  */
17228892ea20SAggelos Economopoulos 
17238892ea20SAggelos Economopoulos static inline void
17248892ea20SAggelos Economopoulos mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src,
17258892ea20SAggelos Economopoulos                   int cnt)
17268892ea20SAggelos Economopoulos {
17278892ea20SAggelos Economopoulos         int idx, i;
17288892ea20SAggelos Economopoulos         uint32_t *src_ints;
17298892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
17308892ea20SAggelos Economopoulos         mcp_kreq_ether_send_t *srcp;
17318892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
17328892ea20SAggelos Economopoulos 	uint8_t last_flags;
17338892ea20SAggelos Economopoulos 
17348892ea20SAggelos Economopoulos         idx = tx->req & tx->mask;
17358892ea20SAggelos Economopoulos 
17368892ea20SAggelos Economopoulos 	last_flags = src->flags;
17378892ea20SAggelos Economopoulos 	src->flags = 0;
17388892ea20SAggelos Economopoulos         wmb();
17398892ea20SAggelos Economopoulos         dst = dstp = &tx->lanai[idx];
17408892ea20SAggelos Economopoulos         srcp = src;
17418892ea20SAggelos Economopoulos 
17428892ea20SAggelos Economopoulos         if ((idx + cnt) < tx->mask) {
17438892ea20SAggelos Economopoulos                 for (i = 0; i < (cnt - 1); i += 2) {
17448892ea20SAggelos Economopoulos                         mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
17458892ea20SAggelos Economopoulos                         wmb(); /* force write every 32 bytes */
17468892ea20SAggelos Economopoulos                         srcp += 2;
17478892ea20SAggelos Economopoulos                         dstp += 2;
17488892ea20SAggelos Economopoulos                 }
17498892ea20SAggelos Economopoulos         } else {
17508892ea20SAggelos Economopoulos                 /* submit all but the first request, and ensure
17518892ea20SAggelos Economopoulos                    that it is submitted below */
17528892ea20SAggelos Economopoulos                 mxge_submit_req_backwards(tx, src, cnt);
17538892ea20SAggelos Economopoulos                 i = 0;
17548892ea20SAggelos Economopoulos         }
17558892ea20SAggelos Economopoulos         if (i < cnt) {
17568892ea20SAggelos Economopoulos                 /* submit the first request */
17578892ea20SAggelos Economopoulos                 mxge_pio_copy(dstp, srcp, sizeof(*src));
17588892ea20SAggelos Economopoulos                 wmb(); /* barrier before setting valid flag */
17598892ea20SAggelos Economopoulos         }
17608892ea20SAggelos Economopoulos 
17618892ea20SAggelos Economopoulos         /* re-write the last 32-bits with the valid flags */
17628892ea20SAggelos Economopoulos         src->flags = last_flags;
17638892ea20SAggelos Economopoulos         src_ints = (uint32_t *)src;
17648892ea20SAggelos Economopoulos         src_ints+=3;
17658892ea20SAggelos Economopoulos         dst_ints = (volatile uint32_t *)dst;
17668892ea20SAggelos Economopoulos         dst_ints+=3;
17678892ea20SAggelos Economopoulos         *dst_ints =  *src_ints;
17688892ea20SAggelos Economopoulos         tx->req += cnt;
17698892ea20SAggelos Economopoulos         wmb();
17708892ea20SAggelos Economopoulos }
17718892ea20SAggelos Economopoulos 
17728892ea20SAggelos Economopoulos #if IFCAP_TSO4
17738892ea20SAggelos Economopoulos 
17748892ea20SAggelos Economopoulos static void
17758892ea20SAggelos Economopoulos mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m,
17768892ea20SAggelos Economopoulos 	       int busdma_seg_cnt, int ip_off)
17778892ea20SAggelos Economopoulos {
17788892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
17798892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
17808892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
17818892ea20SAggelos Economopoulos 	struct ip *ip;
17828892ea20SAggelos Economopoulos 	struct tcphdr *tcp;
17838892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
17848892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
17858892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
17868892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
17878892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
17888892ea20SAggelos Economopoulos 	static int once;
17898892ea20SAggelos Economopoulos 
17908892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
17918892ea20SAggelos Economopoulos 
17928892ea20SAggelos Economopoulos 	/* negative cum_len signifies to the
17938892ea20SAggelos Economopoulos 	 * send loop that we are still in the
17948892ea20SAggelos Economopoulos 	 * header portion of the TSO packet.
17958892ea20SAggelos Economopoulos 	 */
17968892ea20SAggelos Economopoulos 
17978892ea20SAggelos Economopoulos 	/* ensure we have the ethernet, IP and TCP
17988892ea20SAggelos Economopoulos 	   header together in the first mbuf, copy
17998892ea20SAggelos Economopoulos 	   it to a scratch buffer if not */
18008892ea20SAggelos Economopoulos 	if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
18018892ea20SAggelos Economopoulos 		m_copydata(m, 0, ip_off + sizeof (*ip),
18028892ea20SAggelos Economopoulos 			   ss->scratch);
18038892ea20SAggelos Economopoulos 		ip = (struct ip *)(ss->scratch + ip_off);
18048892ea20SAggelos Economopoulos 	} else {
18058892ea20SAggelos Economopoulos 		ip = (struct ip *)(mtod(m, char *) + ip_off);
18068892ea20SAggelos Economopoulos 	}
18078892ea20SAggelos Economopoulos 	if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2)
18088892ea20SAggelos Economopoulos 			    + sizeof (*tcp))) {
18098892ea20SAggelos Economopoulos 		m_copydata(m, 0, ip_off + (ip->ip_hl << 2)
18108892ea20SAggelos Economopoulos 			   + sizeof (*tcp),  ss->scratch);
18118892ea20SAggelos Economopoulos 		ip = (struct ip *)(mtod(m, char *) + ip_off);
18128892ea20SAggelos Economopoulos 	}
18138892ea20SAggelos Economopoulos 
18148892ea20SAggelos Economopoulos 	tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
18158892ea20SAggelos Economopoulos 	cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2));
18168892ea20SAggelos Economopoulos 
18178892ea20SAggelos Economopoulos 	/* TSO implies checksum offload on this hardware */
18188892ea20SAggelos Economopoulos 	cksum_offset = ip_off + (ip->ip_hl << 2);
18198892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
18208892ea20SAggelos Economopoulos 
18218892ea20SAggelos Economopoulos 
18228892ea20SAggelos Economopoulos 	/* for TSO, pseudo_hdr_offset holds mss.
18238892ea20SAggelos Economopoulos 	 * The firmware figures out where to put
18248892ea20SAggelos Economopoulos 	 * the checksum by parsing the header. */
18258892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
18268892ea20SAggelos Economopoulos 
18278892ea20SAggelos Economopoulos 	tx = &ss->tx;
18288892ea20SAggelos Economopoulos 	req = tx->req_list;
18298892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18308892ea20SAggelos Economopoulos 	cnt = 0;
18318892ea20SAggelos Economopoulos 	rdma_count = 0;
18328892ea20SAggelos Economopoulos 	/* "rdma_count" is the number of RDMAs belonging to the
18338892ea20SAggelos Economopoulos 	 * current packet BEFORE the current send request. For
18348892ea20SAggelos Economopoulos 	 * non-TSO packets, this is equal to "count".
18358892ea20SAggelos Economopoulos 	 * For TSO packets, rdma_count needs to be reset
18368892ea20SAggelos Economopoulos 	 * to 0 after a segment cut.
18378892ea20SAggelos Economopoulos 	 *
18388892ea20SAggelos Economopoulos 	 * The rdma_count field of the send request is
18398892ea20SAggelos Economopoulos 	 * the number of RDMAs of the packet starting at
18408892ea20SAggelos Economopoulos 	 * that request. For TSO send requests with one ore more cuts
18418892ea20SAggelos Economopoulos 	 * in the middle, this is the number of RDMAs starting
18428892ea20SAggelos Economopoulos 	 * after the last cut in the request. All previous
18438892ea20SAggelos Economopoulos 	 * segments before the last cut implicitly have 1 RDMA.
18448892ea20SAggelos Economopoulos 	 *
18458892ea20SAggelos Economopoulos 	 * Since the number of RDMAs is not known beforehand,
18468892ea20SAggelos Economopoulos 	 * it must be filled-in retroactively - after each
18478892ea20SAggelos Economopoulos 	 * segmentation cut or at the end of the entire packet.
18488892ea20SAggelos Economopoulos 	 */
18498892ea20SAggelos Economopoulos 
18508892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
18518892ea20SAggelos Economopoulos 		/* Break the busdma segment up into pieces*/
18528892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
18538892ea20SAggelos Economopoulos 		high_swapped = 	htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18548892ea20SAggelos Economopoulos 		len = seg->ds_len;
18558892ea20SAggelos Economopoulos 
18568892ea20SAggelos Economopoulos 		while (len) {
18578892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
18588892ea20SAggelos Economopoulos 			seglen = len;
18598892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
18608892ea20SAggelos Economopoulos 			(req-rdma_count)->rdma_count = rdma_count + 1;
18618892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
18628892ea20SAggelos Economopoulos 				/* payload */
18638892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
18648892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
18658892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
18668892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
18678892ea20SAggelos Economopoulos 				flags_next |= next_is_first *
18688892ea20SAggelos Economopoulos 					MXGEFW_FLAGS_FIRST;
18698892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
18708892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
18718892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
18728892ea20SAggelos Economopoulos 				/* header ends */
18738892ea20SAggelos Economopoulos 				rdma_count = -1;
18748892ea20SAggelos Economopoulos 				cum_len_next = 0;
18758892ea20SAggelos Economopoulos 				seglen = -cum_len;
18768892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
18778892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
18788892ea20SAggelos Economopoulos 					MXGEFW_FLAGS_FIRST |
18798892ea20SAggelos Economopoulos 					(small * MXGEFW_FLAGS_SMALL);
18808892ea20SAggelos Economopoulos 			    }
18818892ea20SAggelos Economopoulos 
18828892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
18838892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
18848892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
18858892ea20SAggelos Economopoulos 			req->pad = 0;
18868892ea20SAggelos Economopoulos 			req->rdma_count = 1;
18878892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
18888892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
18898892ea20SAggelos Economopoulos 			req->flags = flags | ((cum_len & 1) *
18908892ea20SAggelos Economopoulos 					      MXGEFW_FLAGS_ALIGN_ODD);
18918892ea20SAggelos Economopoulos 			low += seglen;
18928892ea20SAggelos Economopoulos 			len -= seglen;
18938892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
18948892ea20SAggelos Economopoulos 			flags = flags_next;
18958892ea20SAggelos Economopoulos 			req++;
18968892ea20SAggelos Economopoulos 			cnt++;
18978892ea20SAggelos Economopoulos 			rdma_count++;
18988892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
18998892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
19008892ea20SAggelos Economopoulos 			else
19018892ea20SAggelos Economopoulos 				cksum_offset = 0;
19028892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
19038892ea20SAggelos Economopoulos 				goto drop;
19048892ea20SAggelos Economopoulos 		}
19058892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
19068892ea20SAggelos Economopoulos 		seg++;
19078892ea20SAggelos Economopoulos 	}
19088892ea20SAggelos Economopoulos 	(req-rdma_count)->rdma_count = rdma_count;
19098892ea20SAggelos Economopoulos 
19108892ea20SAggelos Economopoulos 	do {
19118892ea20SAggelos Economopoulos 		req--;
19128892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
19138892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
19148892ea20SAggelos Economopoulos 
19158892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19168892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19178892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19188892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19198892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19208892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19218892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19228892ea20SAggelos Economopoulos 		tx->activate++;
19238892ea20SAggelos Economopoulos 		wmb();
19248892ea20SAggelos Economopoulos 	}
19258892ea20SAggelos Economopoulos #endif
19268892ea20SAggelos Economopoulos 	return;
19278892ea20SAggelos Economopoulos 
19288892ea20SAggelos Economopoulos drop:
19298892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
19308892ea20SAggelos Economopoulos 	m_freem(m);
19318892ea20SAggelos Economopoulos 	ss->oerrors++;
19328892ea20SAggelos Economopoulos 	if (!once) {
19336c348da6SAggelos Economopoulos 		kprintf("tx->max_desc exceeded via TSO!\n");
19346c348da6SAggelos Economopoulos 		kprintf("mss = %d, %ld, %d!\n", mss,
19358892ea20SAggelos Economopoulos 		       (long)seg - (long)tx->seg_list, tx->max_desc);
19368892ea20SAggelos Economopoulos 		once = 1;
19378892ea20SAggelos Economopoulos 	}
19388892ea20SAggelos Economopoulos 	return;
19398892ea20SAggelos Economopoulos 
19408892ea20SAggelos Economopoulos }
19418892ea20SAggelos Economopoulos 
19428892ea20SAggelos Economopoulos #endif /* IFCAP_TSO4 */
19438892ea20SAggelos Economopoulos 
19448892ea20SAggelos Economopoulos #ifdef MXGE_NEW_VLAN_API
19458892ea20SAggelos Economopoulos /*
19468892ea20SAggelos Economopoulos  * We reproduce the software vlan tag insertion from
19478892ea20SAggelos Economopoulos  * net/if_vlan.c:vlan_start() here so that we can advertise "hardware"
19488892ea20SAggelos Economopoulos  * vlan tag insertion. We need to advertise this in order to have the
19498892ea20SAggelos Economopoulos  * vlan interface respect our csum offload flags.
19508892ea20SAggelos Economopoulos  */
19518892ea20SAggelos Economopoulos static struct mbuf *
19528892ea20SAggelos Economopoulos mxge_vlan_tag_insert(struct mbuf *m)
19538892ea20SAggelos Economopoulos {
19548892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
19558892ea20SAggelos Economopoulos 
1956b915556eSAggelos Economopoulos 	M_PREPEND(m, EVL_ENCAPLEN, MB_DONTWAIT);
19578892ea20SAggelos Economopoulos 	if (__predict_false(m == NULL))
19588892ea20SAggelos Economopoulos 		return NULL;
19598892ea20SAggelos Economopoulos 	if (m->m_len < sizeof(*evl)) {
19608892ea20SAggelos Economopoulos 		m = m_pullup(m, sizeof(*evl));
19618892ea20SAggelos Economopoulos 		if (__predict_false(m == NULL))
19628892ea20SAggelos Economopoulos 			return NULL;
19638892ea20SAggelos Economopoulos 	}
19648892ea20SAggelos Economopoulos 	/*
19658892ea20SAggelos Economopoulos 	 * Transform the Ethernet header into an Ethernet header
19668892ea20SAggelos Economopoulos 	 * with 802.1Q encapsulation.
19678892ea20SAggelos Economopoulos 	 */
19688892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
1969b915556eSAggelos Economopoulos 	bcopy((char *)evl + EVL_ENCAPLEN,
19708892ea20SAggelos Economopoulos 	      (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN);
19718892ea20SAggelos Economopoulos 	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
1972b915556eSAggelos Economopoulos 	evl->evl_tag = htons(m->m_pkthdr.ether_vlantag);
19738892ea20SAggelos Economopoulos 	m->m_flags &= ~M_VLANTAG;
19748892ea20SAggelos Economopoulos 	return m;
19758892ea20SAggelos Economopoulos }
19768892ea20SAggelos Economopoulos #endif /* MXGE_NEW_VLAN_API */
19778892ea20SAggelos Economopoulos 
19788892ea20SAggelos Economopoulos static void
19798892ea20SAggelos Economopoulos mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
19808892ea20SAggelos Economopoulos {
19818892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
19828892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
19838892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
19848892ea20SAggelos Economopoulos 	struct mbuf *m_tmp;
19858892ea20SAggelos Economopoulos 	struct ifnet *ifp;
19868892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
19878892ea20SAggelos Economopoulos 	struct ip *ip;
19888892ea20SAggelos Economopoulos 	int cnt, cum_len, err, i, idx, odd_flag, ip_off;
19898892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
19908892ea20SAggelos Economopoulos         uint8_t flags, cksum_offset;
19918892ea20SAggelos Economopoulos 
19928892ea20SAggelos Economopoulos 
19938892ea20SAggelos Economopoulos 	sc = ss->sc;
19948892ea20SAggelos Economopoulos 	ifp = sc->ifp;
19958892ea20SAggelos Economopoulos 	tx = &ss->tx;
19968892ea20SAggelos Economopoulos 
19978892ea20SAggelos Economopoulos 	ip_off = sizeof (struct ether_header);
19988892ea20SAggelos Economopoulos #ifdef MXGE_NEW_VLAN_API
19998892ea20SAggelos Economopoulos 	if (m->m_flags & M_VLANTAG) {
20008892ea20SAggelos Economopoulos 		m = mxge_vlan_tag_insert(m);
20018892ea20SAggelos Economopoulos 		if (__predict_false(m == NULL))
20028892ea20SAggelos Economopoulos 			goto drop;
2003b915556eSAggelos Economopoulos 		ip_off += EVL_ENCAPLEN;
20048892ea20SAggelos Economopoulos 	}
20058892ea20SAggelos Economopoulos #endif
20068892ea20SAggelos Economopoulos 	/* (try to) map the frame for DMA */
20078892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
20087d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(tx->dmat, tx->info[idx].map,
20097d8771d4SAggelos Economopoulos 					   m, tx->seg_list, 1, &cnt,
20108892ea20SAggelos Economopoulos 					   BUS_DMA_NOWAIT);
20118892ea20SAggelos Economopoulos 	if (__predict_false(err == EFBIG)) {
20128892ea20SAggelos Economopoulos 		/* Too many segments in the chain.  Try
20138892ea20SAggelos Economopoulos 		   to defrag */
20148892ea20SAggelos Economopoulos 		m_tmp = m_defrag(m, M_NOWAIT);
20158892ea20SAggelos Economopoulos 		if (m_tmp == NULL) {
20168892ea20SAggelos Economopoulos 			goto drop;
20178892ea20SAggelos Economopoulos 		}
20188892ea20SAggelos Economopoulos 		ss->tx.defrag++;
20198892ea20SAggelos Economopoulos 		m = m_tmp;
20207d8771d4SAggelos Economopoulos 		err = bus_dmamap_load_mbuf_segment(tx->dmat,
20218892ea20SAggelos Economopoulos 					      tx->info[idx].map,
20227d8771d4SAggelos Economopoulos 					      m, tx->seg_list, 1, &cnt,
20238892ea20SAggelos Economopoulos 					      BUS_DMA_NOWAIT);
20248892ea20SAggelos Economopoulos 	}
20258892ea20SAggelos Economopoulos 	if (__predict_false(err != 0)) {
20267d8771d4SAggelos Economopoulos 		device_printf(sc->dev, "bus_dmamap_load_mbuf_segment returned %d"
20278892ea20SAggelos Economopoulos 			      " packet len = %d\n", err, m->m_pkthdr.len);
20288892ea20SAggelos Economopoulos 		goto drop;
20298892ea20SAggelos Economopoulos 	}
20308892ea20SAggelos Economopoulos 	bus_dmamap_sync(tx->dmat, tx->info[idx].map,
20318892ea20SAggelos Economopoulos 			BUS_DMASYNC_PREWRITE);
20328892ea20SAggelos Economopoulos 	tx->info[idx].m = m;
20338892ea20SAggelos Economopoulos 
20348892ea20SAggelos Economopoulos #if IFCAP_TSO4
20358892ea20SAggelos Economopoulos 	/* TSO is different enough, we handle it in another routine */
20368892ea20SAggelos Economopoulos 	if (m->m_pkthdr.csum_flags & (CSUM_TSO)) {
20378892ea20SAggelos Economopoulos 		mxge_encap_tso(ss, m, cnt, ip_off);
20388892ea20SAggelos Economopoulos 		return;
20398892ea20SAggelos Economopoulos 	}
20408892ea20SAggelos Economopoulos #endif
20418892ea20SAggelos Economopoulos 
20428892ea20SAggelos Economopoulos 	req = tx->req_list;
20438892ea20SAggelos Economopoulos 	cksum_offset = 0;
20448892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
20458892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
20468892ea20SAggelos Economopoulos 
20478892ea20SAggelos Economopoulos 	/* checksum offloading? */
20488892ea20SAggelos Economopoulos 	if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) {
20498892ea20SAggelos Economopoulos 		/* ensure ip header is in first mbuf, copy
20508892ea20SAggelos Economopoulos 		   it to a scratch buffer if not */
20518892ea20SAggelos Economopoulos 		if (__predict_false(m->m_len < ip_off + sizeof (*ip))) {
20528892ea20SAggelos Economopoulos 			m_copydata(m, 0, ip_off + sizeof (*ip),
20538892ea20SAggelos Economopoulos 				   ss->scratch);
20548892ea20SAggelos Economopoulos 			ip = (struct ip *)(ss->scratch + ip_off);
20558892ea20SAggelos Economopoulos 		} else {
20568892ea20SAggelos Economopoulos 			ip = (struct ip *)(mtod(m, char *) + ip_off);
20578892ea20SAggelos Economopoulos 		}
20588892ea20SAggelos Economopoulos 		cksum_offset = ip_off + (ip->ip_hl << 2);
20598892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
20608892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
20618892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
20628892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
20638892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
20648892ea20SAggelos Economopoulos 	} else {
20658892ea20SAggelos Economopoulos 		odd_flag = 0;
20668892ea20SAggelos Economopoulos 	}
20678892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
20688892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
20698892ea20SAggelos Economopoulos 
20708892ea20SAggelos Economopoulos 	/* convert segments into a request list */
20718892ea20SAggelos Economopoulos 	cum_len = 0;
20728892ea20SAggelos Economopoulos 	seg = tx->seg_list;
20738892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
20748892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
20758892ea20SAggelos Economopoulos 		req->addr_low =
20768892ea20SAggelos Economopoulos 			htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
20778892ea20SAggelos Economopoulos 		req->addr_high =
20788892ea20SAggelos Economopoulos 			htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
20798892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
20808892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
20818892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
20828892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
20838892ea20SAggelos Economopoulos 		else
20848892ea20SAggelos Economopoulos 			cksum_offset = 0;
20858892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
20868892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
20878892ea20SAggelos Economopoulos 		req->rdma_count = 1;
20888892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
20898892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
20908892ea20SAggelos Economopoulos 		seg++;
20918892ea20SAggelos Economopoulos 		req++;
20928892ea20SAggelos Economopoulos 		req->flags = 0;
20938892ea20SAggelos Economopoulos 	}
20948892ea20SAggelos Economopoulos 	req--;
20958892ea20SAggelos Economopoulos 	/* pad runts to 60 bytes */
20968892ea20SAggelos Economopoulos 	if (cum_len < 60) {
20978892ea20SAggelos Economopoulos 		req++;
20988892ea20SAggelos Economopoulos 		req->addr_low =
20998892ea20SAggelos Economopoulos 			htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr));
21008892ea20SAggelos Economopoulos 		req->addr_high =
21018892ea20SAggelos Economopoulos 			htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr));
21028892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
21038892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
21048892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
21058892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
21068892ea20SAggelos Economopoulos 		req->rdma_count = 1;
21078892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
21088892ea20SAggelos Economopoulos 		cnt++;
21098892ea20SAggelos Economopoulos 	}
21108892ea20SAggelos Economopoulos 
21118892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
21128892ea20SAggelos Economopoulos #if 0
21138892ea20SAggelos Economopoulos 	/* print what the firmware will see */
21148892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
21156c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
21168892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
21178892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
21188892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
21198892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
21208892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
21218892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
21228892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
21238892ea20SAggelos Economopoulos 	}
21246c348da6SAggelos Economopoulos 	kprintf("--------------\n");
21258892ea20SAggelos Economopoulos #endif
21268892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
21278892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
21288892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
21298892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
21308892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
21318892ea20SAggelos Economopoulos 		*tx->send_go = 1;
21328892ea20SAggelos Economopoulos 		tx->queue_active = 1;
21338892ea20SAggelos Economopoulos 		tx->activate++;
21348892ea20SAggelos Economopoulos 		wmb();
21358892ea20SAggelos Economopoulos 	}
21368892ea20SAggelos Economopoulos #endif
21378892ea20SAggelos Economopoulos 	return;
21388892ea20SAggelos Economopoulos 
21398892ea20SAggelos Economopoulos drop:
21408892ea20SAggelos Economopoulos 	m_freem(m);
21418892ea20SAggelos Economopoulos 	ss->oerrors++;
21428892ea20SAggelos Economopoulos 	return;
21438892ea20SAggelos Economopoulos }
21448892ea20SAggelos Economopoulos 
21458892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
21468892ea20SAggelos Economopoulos static void
21478892ea20SAggelos Economopoulos mxge_qflush(struct ifnet *ifp)
21488892ea20SAggelos Economopoulos {
21498892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
21508892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
21518892ea20SAggelos Economopoulos 	struct mbuf *m;
21528892ea20SAggelos Economopoulos 	int slice;
21538892ea20SAggelos Economopoulos 
21548892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
21558892ea20SAggelos Economopoulos 		tx = &sc->ss[slice].tx;
21562e8181d0SAggelos Economopoulos 		lwkt_serialize_enter(sc->ifp->if_serializer);
21578892ea20SAggelos Economopoulos 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
21588892ea20SAggelos Economopoulos 			m_freem(m);
21592e8181d0SAggelos Economopoulos 		lwkt_serialize_exit(sc->ifp->if_serializer);
21608892ea20SAggelos Economopoulos 	}
21618892ea20SAggelos Economopoulos 	if_qflush(ifp);
21628892ea20SAggelos Economopoulos }
21638892ea20SAggelos Economopoulos 
21648892ea20SAggelos Economopoulos static inline void
21658892ea20SAggelos Economopoulos mxge_start_locked(struct mxge_slice_state *ss)
21668892ea20SAggelos Economopoulos {
21678892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
21688892ea20SAggelos Economopoulos 	struct mbuf *m;
21698892ea20SAggelos Economopoulos 	struct ifnet *ifp;
21708892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
21718892ea20SAggelos Economopoulos 
21728892ea20SAggelos Economopoulos 	sc = ss->sc;
21738892ea20SAggelos Economopoulos 	ifp = sc->ifp;
21748892ea20SAggelos Economopoulos 	tx = &ss->tx;
21758892ea20SAggelos Economopoulos 
21768892ea20SAggelos Economopoulos 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
21778892ea20SAggelos Economopoulos 		m = drbr_dequeue(ifp, tx->br);
21788892ea20SAggelos Economopoulos 		if (m == NULL) {
21798892ea20SAggelos Economopoulos 			return;
21808892ea20SAggelos Economopoulos 		}
21818892ea20SAggelos Economopoulos 		/* let BPF see it */
21828892ea20SAggelos Economopoulos 		BPF_MTAP(ifp, m);
21838892ea20SAggelos Economopoulos 
21848892ea20SAggelos Economopoulos 		/* give it to the nic */
21858892ea20SAggelos Economopoulos 		mxge_encap(ss, m);
21868892ea20SAggelos Economopoulos 	}
21878892ea20SAggelos Economopoulos 	/* ran out of transmit slots */
21882ab1b8a9SAggelos Economopoulos 	if (((ss->if_flags & IFF_OACTIVE) == 0)
21898892ea20SAggelos Economopoulos 	    && (!drbr_empty(ifp, tx->br))) {
21902ab1b8a9SAggelos Economopoulos 		ss->if_flags |= IFF_OACTIVE;
21918892ea20SAggelos Economopoulos 		tx->stall++;
21928892ea20SAggelos Economopoulos 	}
21938892ea20SAggelos Economopoulos }
21948892ea20SAggelos Economopoulos 
21958892ea20SAggelos Economopoulos static int
21968892ea20SAggelos Economopoulos mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m)
21978892ea20SAggelos Economopoulos {
21988892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
21998892ea20SAggelos Economopoulos 	struct ifnet *ifp;
22008892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
22018892ea20SAggelos Economopoulos 	int err;
22028892ea20SAggelos Economopoulos 
22038892ea20SAggelos Economopoulos 	sc = ss->sc;
22048892ea20SAggelos Economopoulos 	ifp = sc->ifp;
22058892ea20SAggelos Economopoulos 	tx = &ss->tx;
22068892ea20SAggelos Economopoulos 
22072ab1b8a9SAggelos Economopoulos 	if ((ss->if_flags & (IFF_RUNNING|IFF_OACTIVE)) !=
22082ab1b8a9SAggelos Economopoulos 	    IFF_RUNNING) {
22098892ea20SAggelos Economopoulos 		err = drbr_enqueue(ifp, tx->br, m);
22108892ea20SAggelos Economopoulos 		return (err);
22118892ea20SAggelos Economopoulos 	}
22128892ea20SAggelos Economopoulos 
22138892ea20SAggelos Economopoulos 	if (drbr_empty(ifp, tx->br) &&
22148892ea20SAggelos Economopoulos 	    ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) {
22158892ea20SAggelos Economopoulos 		/* let BPF see it */
22168892ea20SAggelos Economopoulos 		BPF_MTAP(ifp, m);
22178892ea20SAggelos Economopoulos 		/* give it to the nic */
22188892ea20SAggelos Economopoulos 		mxge_encap(ss, m);
22198892ea20SAggelos Economopoulos 	} else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) {
22208892ea20SAggelos Economopoulos 		return (err);
22218892ea20SAggelos Economopoulos 	}
22228892ea20SAggelos Economopoulos 	if (!drbr_empty(ifp, tx->br))
22238892ea20SAggelos Economopoulos 		mxge_start_locked(ss);
22248892ea20SAggelos Economopoulos 	return (0);
22258892ea20SAggelos Economopoulos }
22268892ea20SAggelos Economopoulos 
22278892ea20SAggelos Economopoulos static int
22288892ea20SAggelos Economopoulos mxge_transmit(struct ifnet *ifp, struct mbuf *m)
22298892ea20SAggelos Economopoulos {
22308892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
22318892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
22328892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
22338892ea20SAggelos Economopoulos 	int err = 0;
22348892ea20SAggelos Economopoulos 	int slice;
22358892ea20SAggelos Economopoulos 
2236deef6e3eSAggelos Economopoulos #if 0
22378892ea20SAggelos Economopoulos 	slice = m->m_pkthdr.flowid;
2238deef6e3eSAggelos Economopoulos #endif
22398892ea20SAggelos Economopoulos 	slice &= (sc->num_slices - 1);  /* num_slices always power of 2 */
22408892ea20SAggelos Economopoulos 
22418892ea20SAggelos Economopoulos 	ss = &sc->ss[slice];
22428892ea20SAggelos Economopoulos 	tx = &ss->tx;
22438892ea20SAggelos Economopoulos 
22442e8181d0SAggelos Economopoulos 	if(lwkt_serialize_try(ifp->if_serializer)) {
22458892ea20SAggelos Economopoulos 		err = mxge_transmit_locked(ss, m);
22462e8181d0SAggelos Economopoulos 		lwkt_serialize_exit(ifp->if_serializer);
22478892ea20SAggelos Economopoulos 	} else {
22488892ea20SAggelos Economopoulos 		err = drbr_enqueue(ifp, tx->br, m);
22498892ea20SAggelos Economopoulos 	}
22508892ea20SAggelos Economopoulos 
22518892ea20SAggelos Economopoulos 	return (err);
22528892ea20SAggelos Economopoulos }
22538892ea20SAggelos Economopoulos 
22548892ea20SAggelos Economopoulos #else
22558892ea20SAggelos Economopoulos 
22568892ea20SAggelos Economopoulos static inline void
22578892ea20SAggelos Economopoulos mxge_start_locked(struct mxge_slice_state *ss)
22588892ea20SAggelos Economopoulos {
22598892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
22608892ea20SAggelos Economopoulos 	struct mbuf *m;
22618892ea20SAggelos Economopoulos 	struct ifnet *ifp;
22628892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
22638892ea20SAggelos Economopoulos 
22648892ea20SAggelos Economopoulos 	sc = ss->sc;
22658892ea20SAggelos Economopoulos 	ifp = sc->ifp;
22668892ea20SAggelos Economopoulos 	tx = &ss->tx;
22678892ea20SAggelos Economopoulos 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
2268f2f758dfSAggelos Economopoulos 		m = ifq_dequeue(&ifp->if_snd, NULL);
22698892ea20SAggelos Economopoulos 		if (m == NULL) {
22708892ea20SAggelos Economopoulos 			return;
22718892ea20SAggelos Economopoulos 		}
22728892ea20SAggelos Economopoulos 		/* let BPF see it */
22738892ea20SAggelos Economopoulos 		BPF_MTAP(ifp, m);
22748892ea20SAggelos Economopoulos 
22758892ea20SAggelos Economopoulos 		/* give it to the nic */
22768892ea20SAggelos Economopoulos 		mxge_encap(ss, m);
22778892ea20SAggelos Economopoulos 	}
22788892ea20SAggelos Economopoulos 	/* ran out of transmit slots */
22792ab1b8a9SAggelos Economopoulos 	if ((sc->ifp->if_flags & IFF_OACTIVE) == 0) {
22802ab1b8a9SAggelos Economopoulos 		sc->ifp->if_flags |= IFF_OACTIVE;
22818892ea20SAggelos Economopoulos 		tx->stall++;
22828892ea20SAggelos Economopoulos 	}
22838892ea20SAggelos Economopoulos }
22848892ea20SAggelos Economopoulos #endif
22858892ea20SAggelos Economopoulos static void
22868892ea20SAggelos Economopoulos mxge_start(struct ifnet *ifp)
22878892ea20SAggelos Economopoulos {
22888892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
22898892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
22908892ea20SAggelos Economopoulos 
22918892ea20SAggelos Economopoulos 	/* only use the first slice for now */
22928892ea20SAggelos Economopoulos 	ss = &sc->ss[0];
22932e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(ifp->if_serializer);
22948892ea20SAggelos Economopoulos 	mxge_start_locked(ss);
22952e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(ifp->if_serializer);
22968892ea20SAggelos Economopoulos }
22978892ea20SAggelos Economopoulos 
22988892ea20SAggelos Economopoulos /*
22998892ea20SAggelos Economopoulos  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
23008892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
23018892ea20SAggelos Economopoulos  * pio handler in the nic.   We re-write the first segment's low
23028892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
23038892ea20SAggelos Economopoulos  * in a burst
23048892ea20SAggelos Economopoulos  */
23058892ea20SAggelos Economopoulos static inline void
23068892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
23078892ea20SAggelos Economopoulos 		mcp_kreq_ether_recv_t *src)
23088892ea20SAggelos Economopoulos {
23098892ea20SAggelos Economopoulos 	uint32_t low;
23108892ea20SAggelos Economopoulos 
23118892ea20SAggelos Economopoulos 	low = src->addr_low;
23128892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
23138892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
23148892ea20SAggelos Economopoulos 	wmb();
23158892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
23168892ea20SAggelos Economopoulos 	wmb();
23178892ea20SAggelos Economopoulos 	src->addr_low = low;
23188892ea20SAggelos Economopoulos 	dst->addr_low = low;
23198892ea20SAggelos Economopoulos 	wmb();
23208892ea20SAggelos Economopoulos }
23218892ea20SAggelos Economopoulos 
23228892ea20SAggelos Economopoulos static int
23238892ea20SAggelos Economopoulos mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
23248892ea20SAggelos Economopoulos {
23258892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
23268892ea20SAggelos Economopoulos 	struct mbuf *m;
23278892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx = &ss->rx_small;
23288892ea20SAggelos Economopoulos 	int cnt, err;
23298892ea20SAggelos Economopoulos 
233017eb0737SAggelos Economopoulos 	m = m_gethdr(MB_DONTWAIT, MT_DATA);
23318892ea20SAggelos Economopoulos 	if (m == NULL) {
23328892ea20SAggelos Economopoulos 		rx->alloc_fail++;
23338892ea20SAggelos Economopoulos 		err = ENOBUFS;
23348892ea20SAggelos Economopoulos 		goto done;
23358892ea20SAggelos Economopoulos 	}
23368892ea20SAggelos Economopoulos 	m->m_len = MHLEN;
23377d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
23387d8771d4SAggelos Economopoulos 				      &seg, 1, &cnt, BUS_DMA_NOWAIT);
23398892ea20SAggelos Economopoulos 	if (err != 0) {
23408892ea20SAggelos Economopoulos 		m_free(m);
23418892ea20SAggelos Economopoulos 		goto done;
23428892ea20SAggelos Economopoulos 	}
23438892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
23448892ea20SAggelos Economopoulos 	rx->shadow[idx].addr_low =
23458892ea20SAggelos Economopoulos 		htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
23468892ea20SAggelos Economopoulos 	rx->shadow[idx].addr_high =
23478892ea20SAggelos Economopoulos 		htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
23488892ea20SAggelos Economopoulos 
23498892ea20SAggelos Economopoulos done:
23508892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
23518892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
23528892ea20SAggelos Economopoulos 	return err;
23538892ea20SAggelos Economopoulos }
23548892ea20SAggelos Economopoulos 
23558892ea20SAggelos Economopoulos static int
23568892ea20SAggelos Economopoulos mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx)
23578892ea20SAggelos Economopoulos {
23588892ea20SAggelos Economopoulos 	bus_dma_segment_t seg[3];
23598892ea20SAggelos Economopoulos 	struct mbuf *m;
23608892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx = &ss->rx_big;
23618892ea20SAggelos Economopoulos 	int cnt, err, i;
23628892ea20SAggelos Economopoulos 
23638892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
236417eb0737SAggelos Economopoulos 		m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
23658892ea20SAggelos Economopoulos 	else
236617eb0737SAggelos Economopoulos 		m = m_getjcl(MB_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size);
23678892ea20SAggelos Economopoulos 	if (m == NULL) {
23688892ea20SAggelos Economopoulos 		rx->alloc_fail++;
23698892ea20SAggelos Economopoulos 		err = ENOBUFS;
23708892ea20SAggelos Economopoulos 		goto done;
23718892ea20SAggelos Economopoulos 	}
23728892ea20SAggelos Economopoulos 	m->m_len = rx->mlen;
23737d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
23747d8771d4SAggelos Economopoulos 				      seg, 1, &cnt, BUS_DMA_NOWAIT);
23758892ea20SAggelos Economopoulos 	if (err != 0) {
23768892ea20SAggelos Economopoulos 		m_free(m);
23778892ea20SAggelos Economopoulos 		goto done;
23788892ea20SAggelos Economopoulos 	}
23798892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
23808892ea20SAggelos Economopoulos 	rx->shadow[idx].addr_low =
23818892ea20SAggelos Economopoulos 		htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
23828892ea20SAggelos Economopoulos 	rx->shadow[idx].addr_high =
23838892ea20SAggelos Economopoulos 		htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
23848892ea20SAggelos Economopoulos 
23858892ea20SAggelos Economopoulos #if MXGE_VIRT_JUMBOS
23868892ea20SAggelos Economopoulos 	for (i = 1; i < cnt; i++) {
23878892ea20SAggelos Economopoulos 		rx->shadow[idx + i].addr_low =
23888892ea20SAggelos Economopoulos 			htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr));
23898892ea20SAggelos Economopoulos 		rx->shadow[idx + i].addr_high =
23908892ea20SAggelos Economopoulos 			htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr));
23918892ea20SAggelos Economopoulos        }
23928892ea20SAggelos Economopoulos #endif
23938892ea20SAggelos Economopoulos 
23948892ea20SAggelos Economopoulos done:
23958892ea20SAggelos Economopoulos        for (i = 0; i < rx->nbufs; i++) {
23968892ea20SAggelos Economopoulos 		if ((idx & 7) == 7) {
23978892ea20SAggelos Economopoulos 			mxge_submit_8rx(&rx->lanai[idx - 7],
23988892ea20SAggelos Economopoulos 					&rx->shadow[idx - 7]);
23998892ea20SAggelos Economopoulos 		}
24008892ea20SAggelos Economopoulos 		idx++;
24018892ea20SAggelos Economopoulos 	}
24028892ea20SAggelos Economopoulos 	return err;
24038892ea20SAggelos Economopoulos }
24048892ea20SAggelos Economopoulos 
24058892ea20SAggelos Economopoulos /*
24068892ea20SAggelos Economopoulos  *  Myri10GE hardware checksums are not valid if the sender
24078892ea20SAggelos Economopoulos  *  padded the frame with non-zero padding.  This is because
24088892ea20SAggelos Economopoulos  *  the firmware just does a simple 16-bit 1s complement
24098892ea20SAggelos Economopoulos  *  checksum across the entire frame, excluding the first 14
24108892ea20SAggelos Economopoulos  *  bytes.  It is best to simply to check the checksum and
24118892ea20SAggelos Economopoulos  *  tell the stack about it only if the checksum is good
24128892ea20SAggelos Economopoulos  */
24138892ea20SAggelos Economopoulos 
24148892ea20SAggelos Economopoulos static inline uint16_t
24158892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
24168892ea20SAggelos Economopoulos {
24178892ea20SAggelos Economopoulos 	struct ether_header *eh;
24188892ea20SAggelos Economopoulos 	struct ip *ip;
24198892ea20SAggelos Economopoulos 	uint16_t c;
24208892ea20SAggelos Economopoulos 
24218892ea20SAggelos Economopoulos 	eh = mtod(m, struct ether_header *);
24228892ea20SAggelos Economopoulos 
24238892ea20SAggelos Economopoulos 	/* only deal with IPv4 TCP & UDP for now */
24248892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
24258892ea20SAggelos Economopoulos 		return 1;
24268892ea20SAggelos Economopoulos 	ip = (struct ip *)(eh + 1);
24278892ea20SAggelos Economopoulos 	if (__predict_false(ip->ip_p != IPPROTO_TCP &&
24288892ea20SAggelos Economopoulos 			    ip->ip_p != IPPROTO_UDP))
24298892ea20SAggelos Economopoulos 		return 1;
24308892ea20SAggelos Economopoulos #ifdef INET
24318892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
24328892ea20SAggelos Economopoulos 		      htonl(ntohs(csum) + ntohs(ip->ip_len) +
24338892ea20SAggelos Economopoulos 			    - (ip->ip_hl << 2) + ip->ip_p));
24348892ea20SAggelos Economopoulos #else
24358892ea20SAggelos Economopoulos 	c = 1;
24368892ea20SAggelos Economopoulos #endif
24378892ea20SAggelos Economopoulos 	c ^= 0xffff;
24388892ea20SAggelos Economopoulos 	return (c);
24398892ea20SAggelos Economopoulos }
24408892ea20SAggelos Economopoulos 
24418892ea20SAggelos Economopoulos static void
24428892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
24438892ea20SAggelos Economopoulos {
24448892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
24458892ea20SAggelos Economopoulos 	struct ether_header *eh;
24468892ea20SAggelos Economopoulos 	uint32_t partial;
24478892ea20SAggelos Economopoulos 
24488892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
24498892ea20SAggelos Economopoulos 	eh = mtod(m, struct ether_header *);
24508892ea20SAggelos Economopoulos 
24518892ea20SAggelos Economopoulos 	/*
2452b915556eSAggelos Economopoulos 	 * fix checksum by subtracting EVL_ENCAPLEN bytes
24538892ea20SAggelos Economopoulos 	 * after what the firmware thought was the end of the ethernet
24548892ea20SAggelos Economopoulos 	 * header.
24558892ea20SAggelos Economopoulos 	 */
24568892ea20SAggelos Economopoulos 
24578892ea20SAggelos Economopoulos 	/* put checksum into host byte order */
24588892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
24598892ea20SAggelos Economopoulos 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
24608892ea20SAggelos Economopoulos 	(*csum) += ~partial;
24618892ea20SAggelos Economopoulos 	(*csum) +=  ((*csum) < ~partial);
24628892ea20SAggelos Economopoulos 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
24638892ea20SAggelos Economopoulos 	(*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF);
24648892ea20SAggelos Economopoulos 
24658892ea20SAggelos Economopoulos 	/* restore checksum to network byte order;
24668892ea20SAggelos Economopoulos 	   later consumers expect this */
24678892ea20SAggelos Economopoulos 	*csum = htons(*csum);
24688892ea20SAggelos Economopoulos 
24698892ea20SAggelos Economopoulos 	/* save the tag */
24708892ea20SAggelos Economopoulos #ifdef MXGE_NEW_VLAN_API
2471b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
24728892ea20SAggelos Economopoulos #else
24738892ea20SAggelos Economopoulos 	{
24748892ea20SAggelos Economopoulos 		struct m_tag *mtag;
24758892ea20SAggelos Economopoulos 		mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int),
2476b915556eSAggelos Economopoulos 				   MB_DONTWAIT);
24778892ea20SAggelos Economopoulos 		if (mtag == NULL)
24788892ea20SAggelos Economopoulos 			return;
24798892ea20SAggelos Economopoulos 		VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag);
24808892ea20SAggelos Economopoulos 		m_tag_prepend(m, mtag);
24818892ea20SAggelos Economopoulos 	}
24828892ea20SAggelos Economopoulos 
24838892ea20SAggelos Economopoulos #endif
24848892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
24858892ea20SAggelos Economopoulos 
24868892ea20SAggelos Economopoulos 	/*
24878892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
24888892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
24898892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
24908892ea20SAggelos Economopoulos 	 * type field is already in place.
24918892ea20SAggelos Economopoulos 	 */
2492b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
24938892ea20SAggelos Economopoulos 	      ETHER_HDR_LEN - ETHER_TYPE_LEN);
2494b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
24958892ea20SAggelos Economopoulos }
24968892ea20SAggelos Economopoulos 
24978892ea20SAggelos Economopoulos 
24988892ea20SAggelos Economopoulos static inline void
24998892ea20SAggelos Economopoulos mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
25008892ea20SAggelos Economopoulos {
25018892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
25028892ea20SAggelos Economopoulos 	struct ifnet *ifp;
25038892ea20SAggelos Economopoulos 	struct mbuf *m;
25048892ea20SAggelos Economopoulos 	struct ether_header *eh;
25058892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
25068892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
25078892ea20SAggelos Economopoulos 	int idx;
25088892ea20SAggelos Economopoulos 	uint16_t tcpudp_csum;
25098892ea20SAggelos Economopoulos 
25108892ea20SAggelos Economopoulos 	sc = ss->sc;
25118892ea20SAggelos Economopoulos 	ifp = sc->ifp;
25128892ea20SAggelos Economopoulos 	rx = &ss->rx_big;
25138892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
25148892ea20SAggelos Economopoulos 	rx->cnt += rx->nbufs;
25158892ea20SAggelos Economopoulos 	/* save a pointer to the received mbuf */
25168892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
25178892ea20SAggelos Economopoulos 	/* try to replace the received mbuf */
25188892ea20SAggelos Economopoulos 	if (mxge_get_buf_big(ss, rx->extra_map, idx)) {
25198892ea20SAggelos Economopoulos 		/* drop the frame -- the old mbuf is re-cycled */
25208892ea20SAggelos Economopoulos 		ifp->if_ierrors++;
25218892ea20SAggelos Economopoulos 		return;
25228892ea20SAggelos Economopoulos 	}
25238892ea20SAggelos Economopoulos 
25248892ea20SAggelos Economopoulos 	/* unmap the received buffer */
25258892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
25268892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
25278892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
25288892ea20SAggelos Economopoulos 
25298892ea20SAggelos Economopoulos 	/* swap the bus_dmamap_t's */
25308892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
25318892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
25328892ea20SAggelos Economopoulos 
25338892ea20SAggelos Economopoulos 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
25348892ea20SAggelos Economopoulos 	 * aligned */
25358892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
25368892ea20SAggelos Economopoulos 
25378892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
25388892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
25398892ea20SAggelos Economopoulos 	ss->ipackets++;
25408892ea20SAggelos Economopoulos 	eh = mtod(m, struct ether_header *);
25418892ea20SAggelos Economopoulos 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
25428892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
25438892ea20SAggelos Economopoulos 	}
25448892ea20SAggelos Economopoulos 	/* if the checksum is valid, mark it in the mbuf header */
25458892ea20SAggelos Economopoulos 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
25468892ea20SAggelos Economopoulos 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
25478892ea20SAggelos Economopoulos 			return;
25488892ea20SAggelos Economopoulos 		/* otherwise, it was a UDP frame, or a TCP frame which
25498892ea20SAggelos Economopoulos 		   we could not do LRO on.  Tell the stack that the
25508892ea20SAggelos Economopoulos 		   checksum is good */
25518892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
25528892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
25538892ea20SAggelos Economopoulos 	}
2554deef6e3eSAggelos Economopoulos #if 0
25558892ea20SAggelos Economopoulos 	/* flowid only valid if RSS hashing is enabled */
25568892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
25578892ea20SAggelos Economopoulos 		m->m_pkthdr.flowid = (ss - sc->ss);
25588892ea20SAggelos Economopoulos 		m->m_flags |= M_FLOWID;
25598892ea20SAggelos Economopoulos 	}
2560deef6e3eSAggelos Economopoulos #endif
25618892ea20SAggelos Economopoulos 	/* pass the frame up the stack */
25628892ea20SAggelos Economopoulos 	(*ifp->if_input)(ifp, m);
25638892ea20SAggelos Economopoulos }
25648892ea20SAggelos Economopoulos 
25658892ea20SAggelos Economopoulos static inline void
25668892ea20SAggelos Economopoulos mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
25678892ea20SAggelos Economopoulos {
25688892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
25698892ea20SAggelos Economopoulos 	struct ifnet *ifp;
25708892ea20SAggelos Economopoulos 	struct ether_header *eh;
25718892ea20SAggelos Economopoulos 	struct mbuf *m;
25728892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
25738892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
25748892ea20SAggelos Economopoulos 	int idx;
25758892ea20SAggelos Economopoulos 	uint16_t tcpudp_csum;
25768892ea20SAggelos Economopoulos 
25778892ea20SAggelos Economopoulos 	sc = ss->sc;
25788892ea20SAggelos Economopoulos 	ifp = sc->ifp;
25798892ea20SAggelos Economopoulos 	rx = &ss->rx_small;
25808892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
25818892ea20SAggelos Economopoulos 	rx->cnt++;
25828892ea20SAggelos Economopoulos 	/* save a pointer to the received mbuf */
25838892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
25848892ea20SAggelos Economopoulos 	/* try to replace the received mbuf */
25858892ea20SAggelos Economopoulos 	if (mxge_get_buf_small(ss, rx->extra_map, idx)) {
25868892ea20SAggelos Economopoulos 		/* drop the frame -- the old mbuf is re-cycled */
25878892ea20SAggelos Economopoulos 		ifp->if_ierrors++;
25888892ea20SAggelos Economopoulos 		return;
25898892ea20SAggelos Economopoulos 	}
25908892ea20SAggelos Economopoulos 
25918892ea20SAggelos Economopoulos 	/* unmap the received buffer */
25928892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
25938892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
25948892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
25958892ea20SAggelos Economopoulos 
25968892ea20SAggelos Economopoulos 	/* swap the bus_dmamap_t's */
25978892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
25988892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
25998892ea20SAggelos Economopoulos 
26008892ea20SAggelos Economopoulos 	/* mcp implicitly skips 1st 2 bytes so that packet is properly
26018892ea20SAggelos Economopoulos 	 * aligned */
26028892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
26038892ea20SAggelos Economopoulos 
26048892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
26058892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
26068892ea20SAggelos Economopoulos 	ss->ipackets++;
26078892ea20SAggelos Economopoulos 	eh = mtod(m, struct ether_header *);
26088892ea20SAggelos Economopoulos 	if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
26098892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
26108892ea20SAggelos Economopoulos 	}
26118892ea20SAggelos Economopoulos 	/* if the checksum is valid, mark it in the mbuf header */
26128892ea20SAggelos Economopoulos 	if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) {
26138892ea20SAggelos Economopoulos 		if (sc->lro_cnt && (0 == mxge_lro_rx(ss, m, csum)))
26148892ea20SAggelos Economopoulos 			return;
26158892ea20SAggelos Economopoulos 		/* otherwise, it was a UDP frame, or a TCP frame which
26168892ea20SAggelos Economopoulos 		   we could not do LRO on.  Tell the stack that the
26178892ea20SAggelos Economopoulos 		   checksum is good */
26188892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
26198892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID;
26208892ea20SAggelos Economopoulos 	}
2621deef6e3eSAggelos Economopoulos #if 0
26228892ea20SAggelos Economopoulos 	/* flowid only valid if RSS hashing is enabled */
26238892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
26248892ea20SAggelos Economopoulos 		m->m_pkthdr.flowid = (ss - sc->ss);
26258892ea20SAggelos Economopoulos 		m->m_flags |= M_FLOWID;
26268892ea20SAggelos Economopoulos 	}
2627deef6e3eSAggelos Economopoulos #endif
26288892ea20SAggelos Economopoulos 	/* pass the frame up the stack */
26298892ea20SAggelos Economopoulos 	(*ifp->if_input)(ifp, m);
26308892ea20SAggelos Economopoulos }
26318892ea20SAggelos Economopoulos 
26328892ea20SAggelos Economopoulos static inline void
26338892ea20SAggelos Economopoulos mxge_clean_rx_done(struct mxge_slice_state *ss)
26348892ea20SAggelos Economopoulos {
26358892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
26368892ea20SAggelos Economopoulos 	int limit = 0;
26378892ea20SAggelos Economopoulos 	uint16_t length;
26388892ea20SAggelos Economopoulos 	uint16_t checksum;
26398892ea20SAggelos Economopoulos 
26408892ea20SAggelos Economopoulos 
26418892ea20SAggelos Economopoulos 	while (rx_done->entry[rx_done->idx].length != 0) {
26428892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
26438892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
26448892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
26458892ea20SAggelos Economopoulos 		if (length <= (MHLEN - MXGEFW_PAD))
26468892ea20SAggelos Economopoulos 			mxge_rx_done_small(ss, length, checksum);
26478892ea20SAggelos Economopoulos 		else
26488892ea20SAggelos Economopoulos 			mxge_rx_done_big(ss, length, checksum);
26498892ea20SAggelos Economopoulos 		rx_done->cnt++;
26508892ea20SAggelos Economopoulos 		rx_done->idx = rx_done->cnt & rx_done->mask;
26518892ea20SAggelos Economopoulos 
26528892ea20SAggelos Economopoulos 		/* limit potential for livelock */
26538892ea20SAggelos Economopoulos 		if (__predict_false(++limit > rx_done->mask / 2))
26548892ea20SAggelos Economopoulos 			break;
26558892ea20SAggelos Economopoulos 	}
26568892ea20SAggelos Economopoulos #ifdef INET
26578892ea20SAggelos Economopoulos 	while (!SLIST_EMPTY(&ss->lro_active)) {
26588892ea20SAggelos Economopoulos 		struct lro_entry *lro = SLIST_FIRST(&ss->lro_active);
26598892ea20SAggelos Economopoulos 		SLIST_REMOVE_HEAD(&ss->lro_active, next);
26608892ea20SAggelos Economopoulos 		mxge_lro_flush(ss, lro);
26618892ea20SAggelos Economopoulos 	}
26628892ea20SAggelos Economopoulos #endif
26638892ea20SAggelos Economopoulos }
26648892ea20SAggelos Economopoulos 
26658892ea20SAggelos Economopoulos 
26668892ea20SAggelos Economopoulos static inline void
26678892ea20SAggelos Economopoulos mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
26688892ea20SAggelos Economopoulos {
26698892ea20SAggelos Economopoulos 	struct ifnet *ifp;
26708892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
26718892ea20SAggelos Economopoulos 	struct mbuf *m;
26728892ea20SAggelos Economopoulos 	bus_dmamap_t map;
26738892ea20SAggelos Economopoulos 	int idx;
26748892ea20SAggelos Economopoulos 	int *flags;
26758892ea20SAggelos Economopoulos 
26768892ea20SAggelos Economopoulos 	tx = &ss->tx;
26778892ea20SAggelos Economopoulos 	ifp = ss->sc->ifp;
26788892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
26798892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
26808892ea20SAggelos Economopoulos 		tx->done++;
26818892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
26828892ea20SAggelos Economopoulos 		/* mbuf and DMA map only attached to the first
26838892ea20SAggelos Economopoulos 		   segment per-mbuf */
26848892ea20SAggelos Economopoulos 		if (m != NULL) {
26858892ea20SAggelos Economopoulos 			ss->obytes += m->m_pkthdr.len;
26868892ea20SAggelos Economopoulos 			if (m->m_flags & M_MCAST)
26878892ea20SAggelos Economopoulos 				ss->omcasts++;
26888892ea20SAggelos Economopoulos 			ss->opackets++;
26898892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
26908892ea20SAggelos Economopoulos 			map = tx->info[idx].map;
26918892ea20SAggelos Economopoulos 			bus_dmamap_unload(tx->dmat, map);
26928892ea20SAggelos Economopoulos 			m_freem(m);
26938892ea20SAggelos Economopoulos 		}
26948892ea20SAggelos Economopoulos 		if (tx->info[idx].flag) {
26958892ea20SAggelos Economopoulos 			tx->info[idx].flag = 0;
26968892ea20SAggelos Economopoulos 			tx->pkt_done++;
26978892ea20SAggelos Economopoulos 		}
26988892ea20SAggelos Economopoulos 	}
26998892ea20SAggelos Economopoulos 
27008892ea20SAggelos Economopoulos 	/* If we have space, clear IFF_OACTIVE to tell the stack that
27018892ea20SAggelos Economopoulos            its OK to send packets */
27028892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
27032ab1b8a9SAggelos Economopoulos 	flags = &ss->if_flags;
27048892ea20SAggelos Economopoulos #else
27052ab1b8a9SAggelos Economopoulos 	flags = &ifp->if_flags;
27068892ea20SAggelos Economopoulos #endif
27072e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(ifp->if_serializer);
27082ab1b8a9SAggelos Economopoulos 	if ((*flags) & IFF_OACTIVE &&
27098892ea20SAggelos Economopoulos 	    tx->req - tx->done < (tx->mask + 1)/4) {
27102ab1b8a9SAggelos Economopoulos 		*(flags) &= ~IFF_OACTIVE;
27118892ea20SAggelos Economopoulos 		ss->tx.wake++;
27128892ea20SAggelos Economopoulos 		mxge_start_locked(ss);
27138892ea20SAggelos Economopoulos 	}
27148892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
27158892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
27168892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
27178892ea20SAggelos Economopoulos 		 * are no more transmits pending */
27188892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
27198892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
27208892ea20SAggelos Economopoulos 			tx->queue_active = 0;
27218892ea20SAggelos Economopoulos 			tx->deactivate++;
27228892ea20SAggelos Economopoulos 			wmb();
27238892ea20SAggelos Economopoulos 		}
27248892ea20SAggelos Economopoulos 	}
27258892ea20SAggelos Economopoulos #endif
27262e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(ifp->if_serializer);
27278892ea20SAggelos Economopoulos 
27288892ea20SAggelos Economopoulos }
27298892ea20SAggelos Economopoulos 
27308892ea20SAggelos Economopoulos static struct mxge_media_type mxge_xfp_media_types[] =
27318892ea20SAggelos Economopoulos {
27328892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
27338892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
27348892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
27358892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
27368892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
27378892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
27388892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
27398892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
27408892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
27418892ea20SAggelos Economopoulos };
27428892ea20SAggelos Economopoulos static struct mxge_media_type mxge_sfp_media_types[] =
27438892ea20SAggelos Economopoulos {
27448892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
27458892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
27468892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
27478892ea20SAggelos Economopoulos 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"}
27488892ea20SAggelos Economopoulos };
27498892ea20SAggelos Economopoulos 
27508892ea20SAggelos Economopoulos static void
27518892ea20SAggelos Economopoulos mxge_set_media(mxge_softc_t *sc, int type)
27528892ea20SAggelos Economopoulos {
27538892ea20SAggelos Economopoulos 	sc->media_flags |= type;
27548892ea20SAggelos Economopoulos 	ifmedia_add(&sc->media, sc->media_flags, 0, NULL);
27558892ea20SAggelos Economopoulos 	ifmedia_set(&sc->media, sc->media_flags);
27568892ea20SAggelos Economopoulos }
27578892ea20SAggelos Economopoulos 
27588892ea20SAggelos Economopoulos 
27598892ea20SAggelos Economopoulos /*
27608892ea20SAggelos Economopoulos  * Determine the media type for a NIC.  Some XFPs will identify
27618892ea20SAggelos Economopoulos  * themselves only when their link is up, so this is initiated via a
27628892ea20SAggelos Economopoulos  * link up interrupt.  However, this can potentially take up to
27638892ea20SAggelos Economopoulos  * several milliseconds, so it is run via the watchdog routine, rather
27648892ea20SAggelos Economopoulos  * than in the interrupt handler itself.   This need only be done
27658892ea20SAggelos Economopoulos  * once, not each time the link is up.
27668892ea20SAggelos Economopoulos  */
27678892ea20SAggelos Economopoulos static void
27688892ea20SAggelos Economopoulos mxge_media_probe(mxge_softc_t *sc)
27698892ea20SAggelos Economopoulos {
27708892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
27718892ea20SAggelos Economopoulos 	char *cage_type;
27728892ea20SAggelos Economopoulos 	char *ptr;
27738892ea20SAggelos Economopoulos 	struct mxge_media_type *mxge_media_types = NULL;
27748892ea20SAggelos Economopoulos 	int i, err, ms, mxge_media_type_entries;
27758892ea20SAggelos Economopoulos 	uint32_t byte;
27768892ea20SAggelos Economopoulos 
27778892ea20SAggelos Economopoulos 	sc->need_media_probe = 0;
27788892ea20SAggelos Economopoulos 
27798892ea20SAggelos Economopoulos 	/* if we've already set a media type, we're done */
27808892ea20SAggelos Economopoulos 	if (sc->media_flags  != (IFM_ETHER | IFM_AUTO))
27818892ea20SAggelos Economopoulos 		return;
27828892ea20SAggelos Economopoulos 
27838892ea20SAggelos Economopoulos 	/*
27848892ea20SAggelos Economopoulos 	 * parse the product code to deterimine the interface type
27858892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
27868892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
27878892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
27888892ea20SAggelos Economopoulos 	 */
27898892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
27908892ea20SAggelos Economopoulos 	if (ptr == NULL) {
27918892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Missing product code\n");
27928892ea20SAggelos Economopoulos 	}
27938892ea20SAggelos Economopoulos 
27948892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
27958892ea20SAggelos Economopoulos 		ptr = index(ptr, '-');
27968892ea20SAggelos Economopoulos 		if (ptr == NULL) {
27978892ea20SAggelos Economopoulos 			device_printf(sc->dev,
27988892ea20SAggelos Economopoulos 				      "only %d dashes in PC?!?\n", i);
27998892ea20SAggelos Economopoulos 			return;
28008892ea20SAggelos Economopoulos 		}
28018892ea20SAggelos Economopoulos 	}
28028892ea20SAggelos Economopoulos 	if (*ptr == 'C') {
28038892ea20SAggelos Economopoulos 		/* -C is CX4 */
28048892ea20SAggelos Economopoulos 		mxge_set_media(sc, IFM_10G_CX4);
28058892ea20SAggelos Economopoulos 		return;
28068892ea20SAggelos Economopoulos 	}
28078892ea20SAggelos Economopoulos 	else if (*ptr == 'Q') {
28088892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
28098892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
28108892ea20SAggelos Economopoulos 		/* FreeBSD has no media type for Quad ribbon fiber */
28118892ea20SAggelos Economopoulos 		return;
28128892ea20SAggelos Economopoulos 	}
28138892ea20SAggelos Economopoulos 
28148892ea20SAggelos Economopoulos 	if (*ptr == 'R') {
28158892ea20SAggelos Economopoulos 		/* -R is XFP */
28168892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
28178892ea20SAggelos Economopoulos 		mxge_media_type_entries =
28188892ea20SAggelos Economopoulos 			sizeof (mxge_xfp_media_types) /
28198892ea20SAggelos Economopoulos 			sizeof (mxge_xfp_media_types[0]);
28208892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
28218892ea20SAggelos Economopoulos 		cage_type = "XFP";
28228892ea20SAggelos Economopoulos 	}
28238892ea20SAggelos Economopoulos 
28248892ea20SAggelos Economopoulos 	if (*ptr == 'S' || *(ptr +1) == 'S') {
28258892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
28268892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
28278892ea20SAggelos Economopoulos 		mxge_media_type_entries =
28288892ea20SAggelos Economopoulos 			sizeof (mxge_sfp_media_types) /
28298892ea20SAggelos Economopoulos 			sizeof (mxge_sfp_media_types[0]);
28308892ea20SAggelos Economopoulos 		cage_type = "SFP+";
28318892ea20SAggelos Economopoulos 		byte = 3;
28328892ea20SAggelos Economopoulos 	}
28338892ea20SAggelos Economopoulos 
28348892ea20SAggelos Economopoulos 	if (mxge_media_types == NULL) {
28358892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
28368892ea20SAggelos Economopoulos 		return;
28378892ea20SAggelos Economopoulos 	}
28388892ea20SAggelos Economopoulos 
28398892ea20SAggelos Economopoulos 	/*
28408892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
28418892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
28428892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
28438892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
28448892ea20SAggelos Economopoulos 	 * a millisecond
28458892ea20SAggelos Economopoulos 	 */
28468892ea20SAggelos Economopoulos 
28478892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
28488892ea20SAggelos Economopoulos 	cmd.data1 = byte;
28498892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
28508892ea20SAggelos Economopoulos 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) {
28518892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to read XFP\n");
28528892ea20SAggelos Economopoulos 	}
28538892ea20SAggelos Economopoulos 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) {
28548892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
28558892ea20SAggelos Economopoulos 	}
28568892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
28578892ea20SAggelos Economopoulos 		return;
28588892ea20SAggelos Economopoulos 	}
28598892ea20SAggelos Economopoulos 
28608892ea20SAggelos Economopoulos 	/* now we wait for the data to be cached */
28618892ea20SAggelos Economopoulos 	cmd.data0 = byte;
28628892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
28638892ea20SAggelos Economopoulos 	for (ms = 0; (err == EBUSY) && (ms < 50); ms++) {
28648892ea20SAggelos Economopoulos 		DELAY(1000);
28658892ea20SAggelos Economopoulos 		cmd.data0 = byte;
28668892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
28678892ea20SAggelos Economopoulos 	}
28688892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
28698892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
28708892ea20SAggelos Economopoulos 			      cage_type, err, ms);
28718892ea20SAggelos Economopoulos 		return;
28728892ea20SAggelos Economopoulos 	}
28738892ea20SAggelos Economopoulos 
28748892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
28758892ea20SAggelos Economopoulos 		if (mxge_verbose)
28768892ea20SAggelos Economopoulos 			device_printf(sc->dev, "%s:%s\n", cage_type,
28778892ea20SAggelos Economopoulos 				      mxge_media_types[0].name);
28788892ea20SAggelos Economopoulos 		mxge_set_media(sc, IFM_10G_CX4);
28798892ea20SAggelos Economopoulos 		return;
28808892ea20SAggelos Economopoulos 	}
28818892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
28828892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
28838892ea20SAggelos Economopoulos 			if (mxge_verbose)
28848892ea20SAggelos Economopoulos 				device_printf(sc->dev, "%s:%s\n",
28858892ea20SAggelos Economopoulos 					      cage_type,
28868892ea20SAggelos Economopoulos 					      mxge_media_types[i].name);
28878892ea20SAggelos Economopoulos 
28888892ea20SAggelos Economopoulos 			mxge_set_media(sc, mxge_media_types[i].flag);
28898892ea20SAggelos Economopoulos 			return;
28908892ea20SAggelos Economopoulos 		}
28918892ea20SAggelos Economopoulos 	}
28928892ea20SAggelos Economopoulos 	device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
28938892ea20SAggelos Economopoulos 		      cmd.data0);
28948892ea20SAggelos Economopoulos 
28958892ea20SAggelos Economopoulos 	return;
28968892ea20SAggelos Economopoulos }
28978892ea20SAggelos Economopoulos 
28988892ea20SAggelos Economopoulos static void
28998892ea20SAggelos Economopoulos mxge_intr(void *arg)
29008892ea20SAggelos Economopoulos {
29018892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss = arg;
29028892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
29038892ea20SAggelos Economopoulos 	mcp_irq_data_t *stats = ss->fw_stats;
29048892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx = &ss->tx;
29058892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
29068892ea20SAggelos Economopoulos 	uint32_t send_done_count;
29078892ea20SAggelos Economopoulos 	uint8_t valid;
29088892ea20SAggelos Economopoulos 
29098892ea20SAggelos Economopoulos 
29108892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
29118892ea20SAggelos Economopoulos 	/* an interrupt on a non-zero slice is implicitly valid
29128892ea20SAggelos Economopoulos 	   since MSI-X irqs are not shared */
29138892ea20SAggelos Economopoulos 	if (ss != sc->ss) {
29148892ea20SAggelos Economopoulos 		mxge_clean_rx_done(ss);
29158892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
29168892ea20SAggelos Economopoulos 		return;
29178892ea20SAggelos Economopoulos 	}
29188892ea20SAggelos Economopoulos #endif
29198892ea20SAggelos Economopoulos 
29208892ea20SAggelos Economopoulos 	/* make sure the DMA has finished */
29218892ea20SAggelos Economopoulos 	if (!stats->valid) {
29228892ea20SAggelos Economopoulos 		return;
29238892ea20SAggelos Economopoulos 	}
29248892ea20SAggelos Economopoulos 	valid = stats->valid;
29258892ea20SAggelos Economopoulos 
29268892ea20SAggelos Economopoulos 	if (sc->legacy_irq) {
29278892ea20SAggelos Economopoulos 		/* lower legacy IRQ  */
29288892ea20SAggelos Economopoulos 		*sc->irq_deassert = 0;
29298892ea20SAggelos Economopoulos 		if (!mxge_deassert_wait)
29308892ea20SAggelos Economopoulos 			/* don't wait for conf. that irq is low */
29318892ea20SAggelos Economopoulos 			stats->valid = 0;
29328892ea20SAggelos Economopoulos 	} else {
29338892ea20SAggelos Economopoulos 		stats->valid = 0;
29348892ea20SAggelos Economopoulos 	}
29358892ea20SAggelos Economopoulos 
29368892ea20SAggelos Economopoulos 	/* loop while waiting for legacy irq deassertion */
29378892ea20SAggelos Economopoulos 	do {
29388892ea20SAggelos Economopoulos 		/* check for transmit completes and receives */
29398892ea20SAggelos Economopoulos 		send_done_count = be32toh(stats->send_done_count);
29408892ea20SAggelos Economopoulos 		while ((send_done_count != tx->pkt_done) ||
29418892ea20SAggelos Economopoulos 		       (rx_done->entry[rx_done->idx].length != 0)) {
29428892ea20SAggelos Economopoulos 			if (send_done_count != tx->pkt_done)
29438892ea20SAggelos Economopoulos 				mxge_tx_done(ss, (int)send_done_count);
29448892ea20SAggelos Economopoulos 			mxge_clean_rx_done(ss);
29458892ea20SAggelos Economopoulos 			send_done_count = be32toh(stats->send_done_count);
29468892ea20SAggelos Economopoulos 		}
29478892ea20SAggelos Economopoulos 		if (sc->legacy_irq && mxge_deassert_wait)
29488892ea20SAggelos Economopoulos 			wmb();
29498892ea20SAggelos Economopoulos 	} while (*((volatile uint8_t *) &stats->valid));
29508892ea20SAggelos Economopoulos 
29518892ea20SAggelos Economopoulos 	/* fw link & error stats meaningful only on the first slice */
29528892ea20SAggelos Economopoulos 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
29538892ea20SAggelos Economopoulos 		if (sc->link_state != stats->link_up) {
29548892ea20SAggelos Economopoulos 			sc->link_state = stats->link_up;
29558892ea20SAggelos Economopoulos 			if (sc->link_state) {
295673a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_UP;
295773a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
29588892ea20SAggelos Economopoulos 				if (mxge_verbose)
29598892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link up\n");
29608892ea20SAggelos Economopoulos 			} else {
296173a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_DOWN;
296273a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
29638892ea20SAggelos Economopoulos 				if (mxge_verbose)
29648892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link down\n");
29658892ea20SAggelos Economopoulos 			}
29668892ea20SAggelos Economopoulos 			sc->need_media_probe = 1;
29678892ea20SAggelos Economopoulos 		}
29688892ea20SAggelos Economopoulos 		if (sc->rdma_tags_available !=
29698892ea20SAggelos Economopoulos 		    be32toh(stats->rdma_tags_available)) {
29708892ea20SAggelos Economopoulos 			sc->rdma_tags_available =
29718892ea20SAggelos Economopoulos 				be32toh(stats->rdma_tags_available);
29728892ea20SAggelos Economopoulos 			device_printf(sc->dev, "RDMA timed out! %d tags "
29738892ea20SAggelos Economopoulos 				      "left\n", sc->rdma_tags_available);
29748892ea20SAggelos Economopoulos 		}
29758892ea20SAggelos Economopoulos 
29768892ea20SAggelos Economopoulos 		if (stats->link_down) {
29778892ea20SAggelos Economopoulos 			sc->down_cnt += stats->link_down;
29788892ea20SAggelos Economopoulos 			sc->link_state = 0;
2979f0115d64SAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
2980f0115d64SAggelos Economopoulos 			if_link_state_change(sc->ifp);
29818892ea20SAggelos Economopoulos 		}
29828892ea20SAggelos Economopoulos 	}
29838892ea20SAggelos Economopoulos 
29848892ea20SAggelos Economopoulos 	/* check to see if we have rx token to pass back */
29858892ea20SAggelos Economopoulos 	if (valid & 0x1)
29868892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
29878892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
29888892ea20SAggelos Economopoulos }
29898892ea20SAggelos Economopoulos 
29908892ea20SAggelos Economopoulos static void
29918892ea20SAggelos Economopoulos mxge_init(void *arg)
29928892ea20SAggelos Economopoulos {
29938892ea20SAggelos Economopoulos }
29948892ea20SAggelos Economopoulos 
29958892ea20SAggelos Economopoulos 
29968892ea20SAggelos Economopoulos 
29978892ea20SAggelos Economopoulos static void
29988892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
29998892ea20SAggelos Economopoulos {
30008892ea20SAggelos Economopoulos 	struct lro_entry *lro_entry;
30018892ea20SAggelos Economopoulos 	int i;
30028892ea20SAggelos Economopoulos 
30038892ea20SAggelos Economopoulos 	while (!SLIST_EMPTY(&ss->lro_free)) {
30048892ea20SAggelos Economopoulos 		lro_entry = SLIST_FIRST(&ss->lro_free);
30058892ea20SAggelos Economopoulos 		SLIST_REMOVE_HEAD(&ss->lro_free, next);
3006d777b84fSAggelos Economopoulos 		kfree(lro_entry, M_DEVBUF);
30078892ea20SAggelos Economopoulos 	}
30088892ea20SAggelos Economopoulos 
30098892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
30108892ea20SAggelos Economopoulos 		if (ss->rx_big.info[i].m == NULL)
30118892ea20SAggelos Economopoulos 			continue;
30128892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->rx_big.dmat,
30138892ea20SAggelos Economopoulos 				  ss->rx_big.info[i].map);
30148892ea20SAggelos Economopoulos 		m_freem(ss->rx_big.info[i].m);
30158892ea20SAggelos Economopoulos 		ss->rx_big.info[i].m = NULL;
30168892ea20SAggelos Economopoulos 	}
30178892ea20SAggelos Economopoulos 
30188892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
30198892ea20SAggelos Economopoulos 		if (ss->rx_small.info[i].m == NULL)
30208892ea20SAggelos Economopoulos 			continue;
30218892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->rx_small.dmat,
30228892ea20SAggelos Economopoulos 				  ss->rx_small.info[i].map);
30238892ea20SAggelos Economopoulos 		m_freem(ss->rx_small.info[i].m);
30248892ea20SAggelos Economopoulos 		ss->rx_small.info[i].m = NULL;
30258892ea20SAggelos Economopoulos 	}
30268892ea20SAggelos Economopoulos 
30278892ea20SAggelos Economopoulos 	/* transmit ring used only on the first slice */
30288892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
30298892ea20SAggelos Economopoulos 		return;
30308892ea20SAggelos Economopoulos 
30318892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
30328892ea20SAggelos Economopoulos 		ss->tx.info[i].flag = 0;
30338892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
30348892ea20SAggelos Economopoulos 			continue;
30358892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->tx.dmat,
30368892ea20SAggelos Economopoulos 				  ss->tx.info[i].map);
30378892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
30388892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
30398892ea20SAggelos Economopoulos 	}
30408892ea20SAggelos Economopoulos }
30418892ea20SAggelos Economopoulos 
30428892ea20SAggelos Economopoulos static void
30438892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
30448892ea20SAggelos Economopoulos {
30458892ea20SAggelos Economopoulos 	int slice;
30468892ea20SAggelos Economopoulos 
30478892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
30488892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
30498892ea20SAggelos Economopoulos }
30508892ea20SAggelos Economopoulos 
30518892ea20SAggelos Economopoulos static void
30528892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
30538892ea20SAggelos Economopoulos {
30548892ea20SAggelos Economopoulos 	int i;
30558892ea20SAggelos Economopoulos 
30568892ea20SAggelos Economopoulos 
30578892ea20SAggelos Economopoulos 	if (ss->rx_done.entry != NULL)
30588892ea20SAggelos Economopoulos 		mxge_dma_free(&ss->rx_done.dma);
30598892ea20SAggelos Economopoulos 	ss->rx_done.entry = NULL;
30608892ea20SAggelos Economopoulos 
30618892ea20SAggelos Economopoulos 	if (ss->tx.req_bytes != NULL)
3062d777b84fSAggelos Economopoulos 		kfree(ss->tx.req_bytes, M_DEVBUF);
30638892ea20SAggelos Economopoulos 	ss->tx.req_bytes = NULL;
30648892ea20SAggelos Economopoulos 
30658892ea20SAggelos Economopoulos 	if (ss->tx.seg_list != NULL)
3066d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
30678892ea20SAggelos Economopoulos 	ss->tx.seg_list = NULL;
30688892ea20SAggelos Economopoulos 
30698892ea20SAggelos Economopoulos 	if (ss->rx_small.shadow != NULL)
3070d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.shadow, M_DEVBUF);
30718892ea20SAggelos Economopoulos 	ss->rx_small.shadow = NULL;
30728892ea20SAggelos Economopoulos 
30738892ea20SAggelos Economopoulos 	if (ss->rx_big.shadow != NULL)
3074d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.shadow, M_DEVBUF);
30758892ea20SAggelos Economopoulos 	ss->rx_big.shadow = NULL;
30768892ea20SAggelos Economopoulos 
30778892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
30788892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
30798892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
30808892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
30818892ea20SAggelos Economopoulos 						   ss->tx.info[i].map);
30828892ea20SAggelos Economopoulos 			}
30838892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
30848892ea20SAggelos Economopoulos 		}
3085d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
30868892ea20SAggelos Economopoulos 	}
30878892ea20SAggelos Economopoulos 	ss->tx.info = NULL;
30888892ea20SAggelos Economopoulos 
30898892ea20SAggelos Economopoulos 	if (ss->rx_small.info != NULL) {
30908892ea20SAggelos Economopoulos 		if (ss->rx_small.dmat != NULL) {
30918892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_small.mask; i++) {
30928892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_small.dmat,
30938892ea20SAggelos Economopoulos 						   ss->rx_small.info[i].map);
30948892ea20SAggelos Economopoulos 			}
30958892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_small.dmat,
30968892ea20SAggelos Economopoulos 					   ss->rx_small.extra_map);
30978892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_small.dmat);
30988892ea20SAggelos Economopoulos 		}
3099d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.info, M_DEVBUF);
31008892ea20SAggelos Economopoulos 	}
31018892ea20SAggelos Economopoulos 	ss->rx_small.info = NULL;
31028892ea20SAggelos Economopoulos 
31038892ea20SAggelos Economopoulos 	if (ss->rx_big.info != NULL) {
31048892ea20SAggelos Economopoulos 		if (ss->rx_big.dmat != NULL) {
31058892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_big.mask; i++) {
31068892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_big.dmat,
31078892ea20SAggelos Economopoulos 						   ss->rx_big.info[i].map);
31088892ea20SAggelos Economopoulos 			}
31098892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_big.dmat,
31108892ea20SAggelos Economopoulos 					   ss->rx_big.extra_map);
31118892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_big.dmat);
31128892ea20SAggelos Economopoulos 		}
3113d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.info, M_DEVBUF);
31148892ea20SAggelos Economopoulos 	}
31158892ea20SAggelos Economopoulos 	ss->rx_big.info = NULL;
31168892ea20SAggelos Economopoulos }
31178892ea20SAggelos Economopoulos 
31188892ea20SAggelos Economopoulos static void
31198892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
31208892ea20SAggelos Economopoulos {
31218892ea20SAggelos Economopoulos 	int slice;
31228892ea20SAggelos Economopoulos 
31238892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
31248892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
31258892ea20SAggelos Economopoulos }
31268892ea20SAggelos Economopoulos 
31278892ea20SAggelos Economopoulos static int
31288892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
31298892ea20SAggelos Economopoulos 		       int tx_ring_entries)
31308892ea20SAggelos Economopoulos {
31318892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
31328892ea20SAggelos Economopoulos 	size_t bytes;
31338892ea20SAggelos Economopoulos 	int err, i;
31348892ea20SAggelos Economopoulos 
31358892ea20SAggelos Economopoulos 	err = ENOMEM;
31368892ea20SAggelos Economopoulos 
31378892ea20SAggelos Economopoulos 	/* allocate per-slice receive resources */
31388892ea20SAggelos Economopoulos 
31398892ea20SAggelos Economopoulos 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
31408892ea20SAggelos Economopoulos 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
31418892ea20SAggelos Economopoulos 
31428892ea20SAggelos Economopoulos 	/* allocate the rx shadow rings */
31438892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow);
3144d777b84fSAggelos Economopoulos 	ss->rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31458892ea20SAggelos Economopoulos 	if (ss->rx_small.shadow == NULL)
31468892ea20SAggelos Economopoulos 		return err;;
31478892ea20SAggelos Economopoulos 
31488892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow);
3149d777b84fSAggelos Economopoulos 	ss->rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31508892ea20SAggelos Economopoulos 	if (ss->rx_big.shadow == NULL)
31518892ea20SAggelos Economopoulos 		return err;;
31528892ea20SAggelos Economopoulos 
31538892ea20SAggelos Economopoulos 	/* allocate the rx host info rings */
31548892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof (*ss->rx_small.info);
3155d777b84fSAggelos Economopoulos 	ss->rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31568892ea20SAggelos Economopoulos 	if (ss->rx_small.info == NULL)
31578892ea20SAggelos Economopoulos 		return err;;
31588892ea20SAggelos Economopoulos 
31598892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof (*ss->rx_big.info);
3160d777b84fSAggelos Economopoulos 	ss->rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31618892ea20SAggelos Economopoulos 	if (ss->rx_big.info == NULL)
31628892ea20SAggelos Economopoulos 		return err;;
31638892ea20SAggelos Economopoulos 
31648892ea20SAggelos Economopoulos 	/* allocate the rx busdma resources */
31658892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
31668892ea20SAggelos Economopoulos 				 1,			/* alignment */
31678892ea20SAggelos Economopoulos 				 4096,			/* boundary */
31688892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
31698892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
31708892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
31718892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
31728892ea20SAggelos Economopoulos 				 1,			/* num segs */
31738892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
31748892ea20SAggelos Economopoulos 				 BUS_DMA_ALLOCNOW,	/* flags */
31758892ea20SAggelos Economopoulos 				 &ss->rx_small.dmat);	/* tag */
31768892ea20SAggelos Economopoulos 	if (err != 0) {
31778892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
31788892ea20SAggelos Economopoulos 			      err);
31798892ea20SAggelos Economopoulos 		return err;;
31808892ea20SAggelos Economopoulos 	}
31818892ea20SAggelos Economopoulos 
31828892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
31838892ea20SAggelos Economopoulos 				 1,			/* alignment */
31848892ea20SAggelos Economopoulos #if MXGE_VIRT_JUMBOS
31858892ea20SAggelos Economopoulos 				 4096,			/* boundary */
31868892ea20SAggelos Economopoulos #else
31878892ea20SAggelos Economopoulos 				 0,			/* boundary */
31888892ea20SAggelos Economopoulos #endif
31898892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
31908892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
31918892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
31928892ea20SAggelos Economopoulos 				 3*4096,		/* maxsize */
31938892ea20SAggelos Economopoulos #if MXGE_VIRT_JUMBOS
31948892ea20SAggelos Economopoulos 				 3,			/* num segs */
31958892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
31968892ea20SAggelos Economopoulos #else
31978892ea20SAggelos Economopoulos 				 1,			/* num segs */
31988892ea20SAggelos Economopoulos 				 MJUM9BYTES,		/* maxsegsize*/
31998892ea20SAggelos Economopoulos #endif
32008892ea20SAggelos Economopoulos 				 BUS_DMA_ALLOCNOW,	/* flags */
32018892ea20SAggelos Economopoulos 				 &ss->rx_big.dmat);	/* tag */
32028892ea20SAggelos Economopoulos 	if (err != 0) {
32038892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
32048892ea20SAggelos Economopoulos 			      err);
32058892ea20SAggelos Economopoulos 		return err;;
32068892ea20SAggelos Economopoulos 	}
32078892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
32088892ea20SAggelos Economopoulos 		err = bus_dmamap_create(ss->rx_small.dmat, 0,
32098892ea20SAggelos Economopoulos 					&ss->rx_small.info[i].map);
32108892ea20SAggelos Economopoulos 		if (err != 0) {
32118892ea20SAggelos Economopoulos 			device_printf(sc->dev, "Err %d  rx_small dmamap\n",
32128892ea20SAggelos Economopoulos 				      err);
32138892ea20SAggelos Economopoulos 			return err;;
32148892ea20SAggelos Economopoulos 		}
32158892ea20SAggelos Economopoulos 	}
32168892ea20SAggelos Economopoulos 	err = bus_dmamap_create(ss->rx_small.dmat, 0,
32178892ea20SAggelos Economopoulos 				&ss->rx_small.extra_map);
32188892ea20SAggelos Economopoulos 	if (err != 0) {
32198892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n",
32208892ea20SAggelos Economopoulos 			      err);
32218892ea20SAggelos Economopoulos 		return err;;
32228892ea20SAggelos Economopoulos 	}
32238892ea20SAggelos Economopoulos 
32248892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
32258892ea20SAggelos Economopoulos 		err = bus_dmamap_create(ss->rx_big.dmat, 0,
32268892ea20SAggelos Economopoulos 					&ss->rx_big.info[i].map);
32278892ea20SAggelos Economopoulos 		if (err != 0) {
32288892ea20SAggelos Economopoulos 			device_printf(sc->dev, "Err %d  rx_big dmamap\n",
32298892ea20SAggelos Economopoulos 				      err);
32308892ea20SAggelos Economopoulos 			return err;;
32318892ea20SAggelos Economopoulos 		}
32328892ea20SAggelos Economopoulos 	}
32338892ea20SAggelos Economopoulos 	err = bus_dmamap_create(ss->rx_big.dmat, 0,
32348892ea20SAggelos Economopoulos 				&ss->rx_big.extra_map);
32358892ea20SAggelos Economopoulos 	if (err != 0) {
32368892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n",
32378892ea20SAggelos Economopoulos 			      err);
32388892ea20SAggelos Economopoulos 		return err;;
32398892ea20SAggelos Economopoulos 	}
32408892ea20SAggelos Economopoulos 
32418892ea20SAggelos Economopoulos 	/* now allocate TX resouces */
32428892ea20SAggelos Economopoulos 
32438892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
32448892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
32458892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
32468892ea20SAggelos Economopoulos 		return 0;
32478892ea20SAggelos Economopoulos #endif
32488892ea20SAggelos Economopoulos 
32498892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
32508892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
32518892ea20SAggelos Economopoulos 
32528892ea20SAggelos Economopoulos 
32538892ea20SAggelos Economopoulos 	/* allocate the tx request copy block */
32548892ea20SAggelos Economopoulos 	bytes = 8 +
32558892ea20SAggelos Economopoulos 		sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4);
3256d777b84fSAggelos Economopoulos 	ss->tx.req_bytes = kmalloc(bytes, M_DEVBUF, M_WAITOK);
32578892ea20SAggelos Economopoulos 	if (ss->tx.req_bytes == NULL)
32588892ea20SAggelos Economopoulos 		return err;;
32598892ea20SAggelos Economopoulos 	/* ensure req_list entries are aligned to 8 bytes */
32608892ea20SAggelos Economopoulos 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
32618892ea20SAggelos Economopoulos 		((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
32628892ea20SAggelos Economopoulos 
32638892ea20SAggelos Economopoulos 	/* allocate the tx busdma segment list */
32648892ea20SAggelos Economopoulos 	bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc;
32658892ea20SAggelos Economopoulos 	ss->tx.seg_list = (bus_dma_segment_t *)
3266d777b84fSAggelos Economopoulos 		kmalloc(bytes, M_DEVBUF, M_WAITOK);
32678892ea20SAggelos Economopoulos 	if (ss->tx.seg_list == NULL)
32688892ea20SAggelos Economopoulos 		return err;;
32698892ea20SAggelos Economopoulos 
32708892ea20SAggelos Economopoulos 	/* allocate the tx host info ring */
32718892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof (*ss->tx.info);
3272d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
32738892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
32748892ea20SAggelos Economopoulos 		return err;;
32758892ea20SAggelos Economopoulos 
32768892ea20SAggelos Economopoulos 	/* allocate the tx busdma resources */
32778892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
32788892ea20SAggelos Economopoulos 				 1,			/* alignment */
32798892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
32808892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
32818892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
32828892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
32838892ea20SAggelos Economopoulos 				 65536 + 256,		/* maxsize */
32848892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
32858892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
32868892ea20SAggelos Economopoulos 				 BUS_DMA_ALLOCNOW,	/* flags */
32878892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
32888892ea20SAggelos Economopoulos 
32898892ea20SAggelos Economopoulos 	if (err != 0) {
32908892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating tx dmat\n",
32918892ea20SAggelos Economopoulos 			      err);
32928892ea20SAggelos Economopoulos 		return err;;
32938892ea20SAggelos Economopoulos 	}
32948892ea20SAggelos Economopoulos 
32958892ea20SAggelos Economopoulos 	/* now use these tags to setup dmamaps for each slot
32968892ea20SAggelos Economopoulos 	   in the ring */
32978892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
32988892ea20SAggelos Economopoulos 		err = bus_dmamap_create(ss->tx.dmat, 0,
32998892ea20SAggelos Economopoulos 					&ss->tx.info[i].map);
33008892ea20SAggelos Economopoulos 		if (err != 0) {
33018892ea20SAggelos Economopoulos 			device_printf(sc->dev, "Err %d  tx dmamap\n",
33028892ea20SAggelos Economopoulos 				      err);
33038892ea20SAggelos Economopoulos 			return err;;
33048892ea20SAggelos Economopoulos 		}
33058892ea20SAggelos Economopoulos 	}
33068892ea20SAggelos Economopoulos 	return 0;
33078892ea20SAggelos Economopoulos 
33088892ea20SAggelos Economopoulos }
33098892ea20SAggelos Economopoulos 
33108892ea20SAggelos Economopoulos static int
33118892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
33128892ea20SAggelos Economopoulos {
33138892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
33148892ea20SAggelos Economopoulos 	int tx_ring_size;
33158892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
33168892ea20SAggelos Economopoulos 	int err, slice;
33178892ea20SAggelos Economopoulos 
33188892ea20SAggelos Economopoulos 	/* get ring sizes */
33198892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
33208892ea20SAggelos Economopoulos 	tx_ring_size = cmd.data0;
33218892ea20SAggelos Economopoulos 	if (err != 0) {
33228892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
33238892ea20SAggelos Economopoulos 		goto abort;
33248892ea20SAggelos Economopoulos 	}
33258892ea20SAggelos Economopoulos 
33268892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t);
33278892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t);
3328f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3329f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
33308892ea20SAggelos Economopoulos 
33318892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
33328892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
33338892ea20SAggelos Economopoulos 					     rx_ring_entries,
33348892ea20SAggelos Economopoulos 					     tx_ring_entries);
33358892ea20SAggelos Economopoulos 		if (err != 0)
33368892ea20SAggelos Economopoulos 			goto abort;
33378892ea20SAggelos Economopoulos 	}
33388892ea20SAggelos Economopoulos 	return 0;
33398892ea20SAggelos Economopoulos 
33408892ea20SAggelos Economopoulos abort:
33418892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
33428892ea20SAggelos Economopoulos 	return err;
33438892ea20SAggelos Economopoulos 
33448892ea20SAggelos Economopoulos }
33458892ea20SAggelos Economopoulos 
33468892ea20SAggelos Economopoulos 
33478892ea20SAggelos Economopoulos static void
33488892ea20SAggelos Economopoulos mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs)
33498892ea20SAggelos Economopoulos {
3350b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
33518892ea20SAggelos Economopoulos 
33528892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
33538892ea20SAggelos Economopoulos 		/* easy, everything fits in a single buffer */
33548892ea20SAggelos Economopoulos 		*big_buf_size = MCLBYTES;
33558892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
33568892ea20SAggelos Economopoulos 		*nbufs = 1;
33578892ea20SAggelos Economopoulos 		return;
33588892ea20SAggelos Economopoulos 	}
33598892ea20SAggelos Economopoulos 
33608892ea20SAggelos Economopoulos 	if (bufsize < MJUMPAGESIZE) {
33618892ea20SAggelos Economopoulos 		/* still easy, everything still fits in a single buffer */
33628892ea20SAggelos Economopoulos 		*big_buf_size = MJUMPAGESIZE;
33638892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
33648892ea20SAggelos Economopoulos 		*nbufs = 1;
33658892ea20SAggelos Economopoulos 		return;
33668892ea20SAggelos Economopoulos 	}
33678892ea20SAggelos Economopoulos #if MXGE_VIRT_JUMBOS
33688892ea20SAggelos Economopoulos 	/* now we need to use virtually contiguous buffers */
33698892ea20SAggelos Economopoulos 	*cl_size = MJUM9BYTES;
33708892ea20SAggelos Economopoulos 	*big_buf_size = 4096;
33718892ea20SAggelos Economopoulos 	*nbufs = mtu / 4096 + 1;
33728892ea20SAggelos Economopoulos 	/* needs to be a power of two, so round up */
33738892ea20SAggelos Economopoulos 	if (*nbufs == 3)
33748892ea20SAggelos Economopoulos 		*nbufs = 4;
33758892ea20SAggelos Economopoulos #else
33768892ea20SAggelos Economopoulos 	*cl_size = MJUM9BYTES;
33778892ea20SAggelos Economopoulos 	*big_buf_size = MJUM9BYTES;
33788892ea20SAggelos Economopoulos 	*nbufs = 1;
33798892ea20SAggelos Economopoulos #endif
33808892ea20SAggelos Economopoulos }
33818892ea20SAggelos Economopoulos 
33828892ea20SAggelos Economopoulos static int
33838892ea20SAggelos Economopoulos mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size)
33848892ea20SAggelos Economopoulos {
33858892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
33868892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
33878892ea20SAggelos Economopoulos 	bus_dmamap_t map;
33888892ea20SAggelos Economopoulos 	struct lro_entry *lro_entry;
33898892ea20SAggelos Economopoulos 	int err, i, slice;
33908892ea20SAggelos Economopoulos 
33918892ea20SAggelos Economopoulos 
33928892ea20SAggelos Economopoulos 	sc = ss->sc;
33938892ea20SAggelos Economopoulos 	slice = ss - sc->ss;
33948892ea20SAggelos Economopoulos 
33958892ea20SAggelos Economopoulos 	SLIST_INIT(&ss->lro_free);
33968892ea20SAggelos Economopoulos 	SLIST_INIT(&ss->lro_active);
33978892ea20SAggelos Economopoulos 
33988892ea20SAggelos Economopoulos 	for (i = 0; i < sc->lro_cnt; i++) {
33998892ea20SAggelos Economopoulos 		lro_entry = (struct lro_entry *)
3400d777b84fSAggelos Economopoulos 			kmalloc(sizeof (*lro_entry), M_DEVBUF,
34018892ea20SAggelos Economopoulos 			       M_NOWAIT | M_ZERO);
34028892ea20SAggelos Economopoulos 		if (lro_entry == NULL) {
34038892ea20SAggelos Economopoulos 			sc->lro_cnt = i;
34048892ea20SAggelos Economopoulos 			break;
34058892ea20SAggelos Economopoulos 		}
34068892ea20SAggelos Economopoulos 		SLIST_INSERT_HEAD(&ss->lro_free, lro_entry, next);
34078892ea20SAggelos Economopoulos 	}
34088892ea20SAggelos Economopoulos 	/* get the lanai pointers to the send and receive rings */
34098892ea20SAggelos Economopoulos 
34108892ea20SAggelos Economopoulos 	err = 0;
34118892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
34128892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
34138892ea20SAggelos Economopoulos 	if (slice == 0) {
34148892ea20SAggelos Economopoulos #endif
34158892ea20SAggelos Economopoulos 		cmd.data0 = slice;
34168892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
34178892ea20SAggelos Economopoulos 		ss->tx.lanai =
34188892ea20SAggelos Economopoulos 			(volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0);
34198892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
34208892ea20SAggelos Economopoulos 			(sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
34218892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
34228892ea20SAggelos Economopoulos 		(sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
34238892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
34248892ea20SAggelos Economopoulos 	}
34258892ea20SAggelos Economopoulos #endif
34268892ea20SAggelos Economopoulos 	cmd.data0 = slice;
34278892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc,
34288892ea20SAggelos Economopoulos 			     MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
34298892ea20SAggelos Economopoulos 	ss->rx_small.lanai =
34308892ea20SAggelos Economopoulos 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34318892ea20SAggelos Economopoulos 	cmd.data0 = slice;
34328892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
34338892ea20SAggelos Economopoulos 	ss->rx_big.lanai =
34348892ea20SAggelos Economopoulos 		(volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0);
34358892ea20SAggelos Economopoulos 
34368892ea20SAggelos Economopoulos 	if (err != 0) {
34378892ea20SAggelos Economopoulos 		device_printf(sc->dev,
34388892ea20SAggelos Economopoulos 			      "failed to get ring sizes or locations\n");
34398892ea20SAggelos Economopoulos 		return EIO;
34408892ea20SAggelos Economopoulos 	}
34418892ea20SAggelos Economopoulos 
34428892ea20SAggelos Economopoulos 	/* stock receive rings */
34438892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
34448892ea20SAggelos Economopoulos 		map = ss->rx_small.info[i].map;
34458892ea20SAggelos Economopoulos 		err = mxge_get_buf_small(ss, map, i);
34468892ea20SAggelos Economopoulos 		if (err) {
34478892ea20SAggelos Economopoulos 			device_printf(sc->dev, "alloced %d/%d smalls\n",
34488892ea20SAggelos Economopoulos 				      i, ss->rx_small.mask + 1);
34498892ea20SAggelos Economopoulos 			return ENOMEM;
34508892ea20SAggelos Economopoulos 		}
34518892ea20SAggelos Economopoulos 	}
34528892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
34538892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
34548892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
34558892ea20SAggelos Economopoulos 	}
34568892ea20SAggelos Economopoulos 	ss->rx_big.nbufs = nbufs;
34578892ea20SAggelos Economopoulos 	ss->rx_big.cl_size = cl_size;
34588892ea20SAggelos Economopoulos 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
3459b915556eSAggelos Economopoulos 		EVL_ENCAPLEN + MXGEFW_PAD;
34608892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) {
34618892ea20SAggelos Economopoulos 		map = ss->rx_big.info[i].map;
34628892ea20SAggelos Economopoulos 		err = mxge_get_buf_big(ss, map, i);
34638892ea20SAggelos Economopoulos 		if (err) {
34648892ea20SAggelos Economopoulos 			device_printf(sc->dev, "alloced %d/%d bigs\n",
34658892ea20SAggelos Economopoulos 				      i, ss->rx_big.mask + 1);
34668892ea20SAggelos Economopoulos 			return ENOMEM;
34678892ea20SAggelos Economopoulos 		}
34688892ea20SAggelos Economopoulos 	}
34698892ea20SAggelos Economopoulos 	return 0;
34708892ea20SAggelos Economopoulos }
34718892ea20SAggelos Economopoulos 
34728892ea20SAggelos Economopoulos static int
34738892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
34748892ea20SAggelos Economopoulos {
34758892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
34768892ea20SAggelos Economopoulos 	int err, big_bytes, nbufs, slice, cl_size, i;
34778892ea20SAggelos Economopoulos 	bus_addr_t bus;
34788892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
34798892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
34808892ea20SAggelos Economopoulos 
34818892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
34828892ea20SAggelos Economopoulos 	bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN);
34838892ea20SAggelos Economopoulos 
34848892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
34858892ea20SAggelos Economopoulos 	if (err != 0) {
34868892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to reset\n");
34878892ea20SAggelos Economopoulos 		return EIO;
34888892ea20SAggelos Economopoulos 	}
34898892ea20SAggelos Economopoulos 
34908892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
34918892ea20SAggelos Economopoulos 		/* setup the indirection table */
34928892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
34938892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE,
34948892ea20SAggelos Economopoulos 				    &cmd);
34958892ea20SAggelos Economopoulos 
34968892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
34978892ea20SAggelos Economopoulos 				     &cmd);
34988892ea20SAggelos Economopoulos 		if (err != 0) {
34998892ea20SAggelos Economopoulos 			device_printf(sc->dev,
35008892ea20SAggelos Economopoulos 				      "failed to setup rss tables\n");
35018892ea20SAggelos Economopoulos 			return err;
35028892ea20SAggelos Economopoulos 		}
35038892ea20SAggelos Economopoulos 
35048892ea20SAggelos Economopoulos 		/* just enable an identity mapping */
35058892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
35068892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
35078892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
35088892ea20SAggelos Economopoulos 
35098892ea20SAggelos Economopoulos 		cmd.data0 = 1;
35108892ea20SAggelos Economopoulos 		cmd.data1 = mxge_rss_hash_type;
35118892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
35128892ea20SAggelos Economopoulos 		if (err != 0) {
35138892ea20SAggelos Economopoulos 			device_printf(sc->dev, "failed to enable slices\n");
35148892ea20SAggelos Economopoulos 			return err;
35158892ea20SAggelos Economopoulos 		}
35168892ea20SAggelos Economopoulos 	}
35178892ea20SAggelos Economopoulos 
35188892ea20SAggelos Economopoulos 
35198892ea20SAggelos Economopoulos 	mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs);
35208892ea20SAggelos Economopoulos 
35218892ea20SAggelos Economopoulos 	cmd.data0 = nbufs;
35228892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
35238892ea20SAggelos Economopoulos 			    &cmd);
35248892ea20SAggelos Economopoulos 	/* error is only meaningful if we're trying to set
35258892ea20SAggelos Economopoulos 	   MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */
35268892ea20SAggelos Economopoulos 	if (err && nbufs > 1) {
35278892ea20SAggelos Economopoulos 		device_printf(sc->dev,
35288892ea20SAggelos Economopoulos 			      "Failed to set alway-use-n to %d\n",
35298892ea20SAggelos Economopoulos 			      nbufs);
35308892ea20SAggelos Economopoulos 		return EIO;
35318892ea20SAggelos Economopoulos 	}
35328892ea20SAggelos Economopoulos 	/* Give the firmware the mtu and the big and small buffer
35338892ea20SAggelos Economopoulos 	   sizes.  The firmware wants the big buf size to be a power
35348892ea20SAggelos Economopoulos 	   of two. Luckily, FreeBSD's clusters are powers of two */
3535b915556eSAggelos Economopoulos 	cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
35368892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
35378892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
35388892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE,
35398892ea20SAggelos Economopoulos 			     &cmd);
35408892ea20SAggelos Economopoulos 	cmd.data0 = big_bytes;
35418892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
35428892ea20SAggelos Economopoulos 
35438892ea20SAggelos Economopoulos 	if (err != 0) {
35448892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to setup params\n");
35458892ea20SAggelos Economopoulos 		goto abort;
35468892ea20SAggelos Economopoulos 	}
35478892ea20SAggelos Economopoulos 
35488892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
35498892ea20SAggelos Economopoulos 	for (slice = 0;
35508892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
35518892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
35528892ea20SAggelos Economopoulos #else
35538892ea20SAggelos Economopoulos 	     slice < 1;
35548892ea20SAggelos Economopoulos #endif
35558892ea20SAggelos Economopoulos 	     slice++) {
35568892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
35578892ea20SAggelos Economopoulos 		cmd.data0 =
35588892ea20SAggelos Economopoulos 			MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr);
35598892ea20SAggelos Economopoulos 		cmd.data1 =
35608892ea20SAggelos Economopoulos 			MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr);
35618892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
35628892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
35638892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
35648892ea20SAggelos Economopoulos 	}
35658892ea20SAggelos Economopoulos 
35668892ea20SAggelos Economopoulos 	if (err != 0) {
35678892ea20SAggelos Economopoulos 		bus = sc->ss->fw_stats_dma.bus_addr;
35688892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
35698892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
35708892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
35718892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc,
35728892ea20SAggelos Economopoulos 				    MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
35738892ea20SAggelos Economopoulos 				    &cmd);
35748892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
35758892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
35768892ea20SAggelos Economopoulos 	} else {
35778892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
35788892ea20SAggelos Economopoulos 	}
35798892ea20SAggelos Economopoulos 
35808892ea20SAggelos Economopoulos 	if (err != 0) {
35818892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to setup params\n");
35828892ea20SAggelos Economopoulos 		goto abort;
35838892ea20SAggelos Economopoulos 	}
35848892ea20SAggelos Economopoulos 
35858892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
35868892ea20SAggelos Economopoulos 		err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size);
35878892ea20SAggelos Economopoulos 		if (err != 0) {
35888892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't open slice %d\n",
35898892ea20SAggelos Economopoulos 				      slice);
35908892ea20SAggelos Economopoulos 			goto abort;
35918892ea20SAggelos Economopoulos 		}
35928892ea20SAggelos Economopoulos 	}
35938892ea20SAggelos Economopoulos 
35948892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
35958892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
35968892ea20SAggelos Economopoulos 	if (err) {
35978892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Couldn't bring up link\n");
35988892ea20SAggelos Economopoulos 		goto abort;
35998892ea20SAggelos Economopoulos 	}
36008892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
36018892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
36028892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
36032ab1b8a9SAggelos Economopoulos 		ss->if_flags |= IFF_RUNNING;
36042ab1b8a9SAggelos Economopoulos 		ss->if_flags &= ~IFF_OACTIVE;
36058892ea20SAggelos Economopoulos 	}
36068892ea20SAggelos Economopoulos #endif
36072ab1b8a9SAggelos Economopoulos 	sc->ifp->if_flags |= IFF_RUNNING;
36082ab1b8a9SAggelos Economopoulos 	sc->ifp->if_flags &= ~IFF_OACTIVE;
36098892ea20SAggelos Economopoulos 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
36108892ea20SAggelos Economopoulos 
36118892ea20SAggelos Economopoulos 	return 0;
36128892ea20SAggelos Economopoulos 
36138892ea20SAggelos Economopoulos 
36148892ea20SAggelos Economopoulos abort:
36158892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
36168892ea20SAggelos Economopoulos 
36178892ea20SAggelos Economopoulos 	return err;
36188892ea20SAggelos Economopoulos }
36198892ea20SAggelos Economopoulos 
36208892ea20SAggelos Economopoulos static int
36218892ea20SAggelos Economopoulos mxge_close(mxge_softc_t *sc)
36228892ea20SAggelos Economopoulos {
36238892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
36248892ea20SAggelos Economopoulos 	int err, old_down_cnt;
36258892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
36268892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
36278892ea20SAggelos Economopoulos 	int slice;
36288892ea20SAggelos Economopoulos #endif
36298892ea20SAggelos Economopoulos 
36308892ea20SAggelos Economopoulos 	callout_stop(&sc->co_hdl);
36318892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
36328892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
36338892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
36342ab1b8a9SAggelos Economopoulos 		ss->if_flags &= ~IFF_RUNNING;
36358892ea20SAggelos Economopoulos 	}
36368892ea20SAggelos Economopoulos #endif
36372ab1b8a9SAggelos Economopoulos 	sc->ifp->if_flags &= ~IFF_RUNNING;
36388892ea20SAggelos Economopoulos 	old_down_cnt = sc->down_cnt;
36398892ea20SAggelos Economopoulos 	wmb();
36408892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
36418892ea20SAggelos Economopoulos 	if (err) {
36428892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Couldn't bring down link\n");
36438892ea20SAggelos Economopoulos 	}
36448892ea20SAggelos Economopoulos 	if (old_down_cnt == sc->down_cnt) {
36458892ea20SAggelos Economopoulos 		/* wait for down irq */
36468892ea20SAggelos Economopoulos 		DELAY(10 * sc->intr_coal_delay);
36478892ea20SAggelos Economopoulos 	}
36488892ea20SAggelos Economopoulos 	wmb();
36498892ea20SAggelos Economopoulos 	if (old_down_cnt == sc->down_cnt) {
36508892ea20SAggelos Economopoulos 		device_printf(sc->dev, "never got down irq\n");
36518892ea20SAggelos Economopoulos 	}
36528892ea20SAggelos Economopoulos 
36538892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
36548892ea20SAggelos Economopoulos 
36558892ea20SAggelos Economopoulos 	return 0;
36568892ea20SAggelos Economopoulos }
36578892ea20SAggelos Economopoulos 
36588892ea20SAggelos Economopoulos static void
36598892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
36608892ea20SAggelos Economopoulos {
36618892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
36628892ea20SAggelos Economopoulos 	int reg;
36638892ea20SAggelos Economopoulos 	uint16_t cmd, lnk, pectl;
36648892ea20SAggelos Economopoulos 
36658892ea20SAggelos Economopoulos 	/* find the PCIe link width and set max read request to 4KB*/
36668892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
36678892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
36688892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
36698892ea20SAggelos Economopoulos 
36708892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
36718892ea20SAggelos Economopoulos 		pectl = (pectl & ~0x7000) | (5 << 12);
36728892ea20SAggelos Economopoulos 		pci_write_config(dev, reg + 0x8, pectl, 2);
36738892ea20SAggelos Economopoulos 	}
36748892ea20SAggelos Economopoulos 
36758892ea20SAggelos Economopoulos 	/* Enable DMA and Memory space access */
36768892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
36778892ea20SAggelos Economopoulos 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
36788892ea20SAggelos Economopoulos 	cmd |= PCIM_CMD_MEMEN;
36798892ea20SAggelos Economopoulos 	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
36808892ea20SAggelos Economopoulos }
36818892ea20SAggelos Economopoulos 
36828892ea20SAggelos Economopoulos static uint32_t
36838892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
36848892ea20SAggelos Economopoulos {
36858892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
36868892ea20SAggelos Economopoulos 	uint32_t vs;
36878892ea20SAggelos Economopoulos 
36888892ea20SAggelos Economopoulos 	/* find the vendor specific offset */
36898892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
36908892ea20SAggelos Economopoulos 		device_printf(sc->dev,
36918892ea20SAggelos Economopoulos 			      "could not find vendor specific offset\n");
36928892ea20SAggelos Economopoulos 		return (uint32_t)-1;
36938892ea20SAggelos Economopoulos 	}
36948892ea20SAggelos Economopoulos 	/* enable read32 mode */
36958892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
36968892ea20SAggelos Economopoulos 	/* tell NIC which register to read */
36978892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
36988892ea20SAggelos Economopoulos 	return (pci_read_config(dev, vs + 0x14, 4));
36998892ea20SAggelos Economopoulos }
37008892ea20SAggelos Economopoulos 
37018892ea20SAggelos Economopoulos static int
37028892ea20SAggelos Economopoulos mxge_watchdog_reset(mxge_softc_t *sc, int slice)
37038892ea20SAggelos Economopoulos {
37048892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
37058892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
37068892ea20SAggelos Economopoulos 	int err;
37078892ea20SAggelos Economopoulos 	uint32_t reboot;
37088892ea20SAggelos Economopoulos 	uint16_t cmd;
37098892ea20SAggelos Economopoulos 
37108892ea20SAggelos Economopoulos 	err = ENXIO;
37118892ea20SAggelos Economopoulos 
37128892ea20SAggelos Economopoulos 	device_printf(sc->dev, "Watchdog reset!\n");
37138892ea20SAggelos Economopoulos 
37148892ea20SAggelos Economopoulos 	/*
37158892ea20SAggelos Economopoulos 	 * check to see if the NIC rebooted.  If it did, then all of
37168892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
37178892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
37188892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
37198892ea20SAggelos Economopoulos 	 * again
37208892ea20SAggelos Economopoulos 	 */
37218892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
37228892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
37238892ea20SAggelos Economopoulos 		/*
37248892ea20SAggelos Economopoulos 		 * maybe the watchdog caught the NIC rebooting; wait
37258892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
37268892ea20SAggelos Economopoulos 		 * back, then give up
37278892ea20SAggelos Economopoulos 		 */
37288892ea20SAggelos Economopoulos 		DELAY(1000*100);
37298892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
37308892ea20SAggelos Economopoulos 		if (cmd == 0xffff) {
37318892ea20SAggelos Economopoulos 			device_printf(sc->dev, "NIC disappeared!\n");
37328892ea20SAggelos Economopoulos 			return (err);
37338892ea20SAggelos Economopoulos 		}
37348892ea20SAggelos Economopoulos 	}
37358892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
37368892ea20SAggelos Economopoulos 		/* print the reboot status */
37378892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
37388892ea20SAggelos Economopoulos 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
37398892ea20SAggelos Economopoulos 			      reboot);
37408892ea20SAggelos Economopoulos 		/* restore PCI configuration space */
37418892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
37428892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
37438892ea20SAggelos Economopoulos 
37448892ea20SAggelos Economopoulos 		/* and redo any changes we made to our config space */
37458892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
37468892ea20SAggelos Economopoulos 
37472ab1b8a9SAggelos Economopoulos 		if (sc->ifp->if_flags & IFF_RUNNING) {
37488892ea20SAggelos Economopoulos 			mxge_close(sc);
37498892ea20SAggelos Economopoulos 			err = mxge_open(sc);
37508892ea20SAggelos Economopoulos 		}
37518892ea20SAggelos Economopoulos 	} else {
37528892ea20SAggelos Economopoulos 		tx = &sc->ss[slice].tx;
37538892ea20SAggelos Economopoulos 		device_printf(sc->dev,
37548892ea20SAggelos Economopoulos 			      "NIC did not reboot, slice %d ring state:\n",
37558892ea20SAggelos Economopoulos 			      slice);
37568892ea20SAggelos Economopoulos 		device_printf(sc->dev,
37578892ea20SAggelos Economopoulos 			      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
37588892ea20SAggelos Economopoulos 			      tx->req, tx->done, tx->queue_active);
37598892ea20SAggelos Economopoulos 		device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
37608892ea20SAggelos Economopoulos 			      tx->activate, tx->deactivate);
37618892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pkt_done=%d fw=%d\n",
37628892ea20SAggelos Economopoulos 			      tx->pkt_done,
37638892ea20SAggelos Economopoulos 			      be32toh(sc->ss->fw_stats->send_done_count));
37648892ea20SAggelos Economopoulos 		device_printf(sc->dev, "not resetting\n");
37658892ea20SAggelos Economopoulos 	}
37668892ea20SAggelos Economopoulos 	return (err);
37678892ea20SAggelos Economopoulos }
37688892ea20SAggelos Economopoulos 
37698892ea20SAggelos Economopoulos static int
37708892ea20SAggelos Economopoulos mxge_watchdog(mxge_softc_t *sc)
37718892ea20SAggelos Economopoulos {
37728892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
37738892ea20SAggelos Economopoulos 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
37748892ea20SAggelos Economopoulos 	int i, err = 0;
37758892ea20SAggelos Economopoulos 
37768892ea20SAggelos Economopoulos 	/* see if we have outstanding transmits, which
37778892ea20SAggelos Economopoulos 	   have been pending for more than mxge_ticks */
37788892ea20SAggelos Economopoulos 	for (i = 0;
37798892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
37808892ea20SAggelos Economopoulos 	     (i < sc->num_slices) && (err == 0);
37818892ea20SAggelos Economopoulos #else
37828892ea20SAggelos Economopoulos 	     (i < 1) && (err == 0);
37838892ea20SAggelos Economopoulos #endif
37848892ea20SAggelos Economopoulos 	     i++) {
37858892ea20SAggelos Economopoulos 		tx = &sc->ss[i].tx;
37868892ea20SAggelos Economopoulos 		if (tx->req != tx->done &&
37878892ea20SAggelos Economopoulos 		    tx->watchdog_req != tx->watchdog_done &&
37888892ea20SAggelos Economopoulos 		    tx->done == tx->watchdog_done) {
37898892ea20SAggelos Economopoulos 			/* check for pause blocking before resetting */
37908892ea20SAggelos Economopoulos 			if (tx->watchdog_rx_pause == rx_pause)
37918892ea20SAggelos Economopoulos 				err = mxge_watchdog_reset(sc, i);
37928892ea20SAggelos Economopoulos 			else
37938892ea20SAggelos Economopoulos 				device_printf(sc->dev, "Flow control blocking "
37948892ea20SAggelos Economopoulos 					      "xmits, check link partner\n");
37958892ea20SAggelos Economopoulos 		}
37968892ea20SAggelos Economopoulos 
37978892ea20SAggelos Economopoulos 		tx->watchdog_req = tx->req;
37988892ea20SAggelos Economopoulos 		tx->watchdog_done = tx->done;
37998892ea20SAggelos Economopoulos 		tx->watchdog_rx_pause = rx_pause;
38008892ea20SAggelos Economopoulos 	}
38018892ea20SAggelos Economopoulos 
38028892ea20SAggelos Economopoulos 	if (sc->need_media_probe)
38038892ea20SAggelos Economopoulos 		mxge_media_probe(sc);
38048892ea20SAggelos Economopoulos 	return (err);
38058892ea20SAggelos Economopoulos }
38068892ea20SAggelos Economopoulos 
38078892ea20SAggelos Economopoulos static void
38088892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
38098892ea20SAggelos Economopoulos {
38108892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
38118892ea20SAggelos Economopoulos 	u_long ipackets = 0;
38128892ea20SAggelos Economopoulos 	u_long opackets = 0;
38138892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
38148892ea20SAggelos Economopoulos 	u_long obytes = 0;
38158892ea20SAggelos Economopoulos 	u_long omcasts = 0;
38168892ea20SAggelos Economopoulos 	u_long odrops = 0;
38178892ea20SAggelos Economopoulos #endif
38188892ea20SAggelos Economopoulos 	u_long oerrors = 0;
38198892ea20SAggelos Economopoulos 	int slice;
38208892ea20SAggelos Economopoulos 
38218892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
38228892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
38238892ea20SAggelos Economopoulos 		ipackets += ss->ipackets;
38248892ea20SAggelos Economopoulos 		opackets += ss->opackets;
38258892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
38268892ea20SAggelos Economopoulos 		obytes += ss->obytes;
38278892ea20SAggelos Economopoulos 		omcasts += ss->omcasts;
38288892ea20SAggelos Economopoulos 		odrops += ss->tx.br->br_drops;
38298892ea20SAggelos Economopoulos #endif
38308892ea20SAggelos Economopoulos 		oerrors += ss->oerrors;
38318892ea20SAggelos Economopoulos 	}
38328892ea20SAggelos Economopoulos 	sc->ifp->if_ipackets = ipackets;
38338892ea20SAggelos Economopoulos 	sc->ifp->if_opackets = opackets;
38348892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
38358892ea20SAggelos Economopoulos 	sc->ifp->if_obytes = obytes;
38368892ea20SAggelos Economopoulos 	sc->ifp->if_omcasts = omcasts;
38378892ea20SAggelos Economopoulos 	sc->ifp->if_snd.ifq_drops = odrops;
38388892ea20SAggelos Economopoulos #endif
38398892ea20SAggelos Economopoulos 	sc->ifp->if_oerrors = oerrors;
38408892ea20SAggelos Economopoulos }
38418892ea20SAggelos Economopoulos 
38428892ea20SAggelos Economopoulos static void
38438892ea20SAggelos Economopoulos mxge_tick(void *arg)
38448892ea20SAggelos Economopoulos {
38458892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
38468892ea20SAggelos Economopoulos 	int err = 0;
38478892ea20SAggelos Economopoulos 
38482e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
38498892ea20SAggelos Economopoulos 	/* aggregate stats from different slices */
38508892ea20SAggelos Economopoulos 	mxge_update_stats(sc);
38518892ea20SAggelos Economopoulos 	if (!sc->watchdog_countdown) {
38528892ea20SAggelos Economopoulos 		err = mxge_watchdog(sc);
38538892ea20SAggelos Economopoulos 		sc->watchdog_countdown = 4;
38548892ea20SAggelos Economopoulos 	}
38558892ea20SAggelos Economopoulos 	sc->watchdog_countdown--;
38568892ea20SAggelos Economopoulos 	if (err == 0)
38578892ea20SAggelos Economopoulos 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
38582e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
38598892ea20SAggelos Economopoulos }
38608892ea20SAggelos Economopoulos 
38618892ea20SAggelos Economopoulos static int
38628892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
38638892ea20SAggelos Economopoulos {
38648892ea20SAggelos Economopoulos 	return EINVAL;
38658892ea20SAggelos Economopoulos }
38668892ea20SAggelos Economopoulos 
38678892ea20SAggelos Economopoulos static int
38688892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
38698892ea20SAggelos Economopoulos {
38708892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
38718892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
38728892ea20SAggelos Economopoulos 	int err = 0;
38738892ea20SAggelos Economopoulos 
38748892ea20SAggelos Economopoulos 
3875b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
38768892ea20SAggelos Economopoulos 	if ((real_mtu > sc->max_mtu) || real_mtu < 60)
38778892ea20SAggelos Economopoulos 		return EINVAL;
38782e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(ifp->if_serializer);
38798892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
38808892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
38812ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
38828892ea20SAggelos Economopoulos 		mxge_close(sc);
38838892ea20SAggelos Economopoulos 		err = mxge_open(sc);
38848892ea20SAggelos Economopoulos 		if (err != 0) {
38858892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
38868892ea20SAggelos Economopoulos 			mxge_close(sc);
38878892ea20SAggelos Economopoulos 			(void) mxge_open(sc);
38888892ea20SAggelos Economopoulos 		}
38898892ea20SAggelos Economopoulos 	}
38902e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(ifp->if_serializer);
38918892ea20SAggelos Economopoulos 	return err;
38928892ea20SAggelos Economopoulos }
38938892ea20SAggelos Economopoulos 
38948892ea20SAggelos Economopoulos static void
38958892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
38968892ea20SAggelos Economopoulos {
38978892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38988892ea20SAggelos Economopoulos 
38998892ea20SAggelos Economopoulos 
39008892ea20SAggelos Economopoulos 	if (sc == NULL)
39018892ea20SAggelos Economopoulos 		return;
39028892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
39038892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
39048892ea20SAggelos Economopoulos 	ifmr->ifm_active = IFM_AUTO | IFM_ETHER;
39058892ea20SAggelos Economopoulos 	ifmr->ifm_active |= sc->link_state ? IFM_FDX : 0;
39068892ea20SAggelos Economopoulos }
39078892ea20SAggelos Economopoulos 
39088892ea20SAggelos Economopoulos static int
3909137195a6SAggelos Economopoulos mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr)
39108892ea20SAggelos Economopoulos {
39118892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
39128892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
39138892ea20SAggelos Economopoulos 	int err, mask;
39148892ea20SAggelos Economopoulos 
3915137195a6SAggelos Economopoulos 	(void)cr;
39168892ea20SAggelos Economopoulos 	err = 0;
39178892ea20SAggelos Economopoulos 	switch (command) {
39188892ea20SAggelos Economopoulos 	case SIOCSIFADDR:
39198892ea20SAggelos Economopoulos 	case SIOCGIFADDR:
39208892ea20SAggelos Economopoulos 		err = ether_ioctl(ifp, command, data);
39218892ea20SAggelos Economopoulos 		break;
39228892ea20SAggelos Economopoulos 
39238892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
39248892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
39258892ea20SAggelos Economopoulos 		break;
39268892ea20SAggelos Economopoulos 
39278892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
39282e8181d0SAggelos Economopoulos 		lwkt_serialize_enter(sc->ifp->if_serializer);
39298892ea20SAggelos Economopoulos 		if (sc->dying) {
39302e8181d0SAggelos Economopoulos 			lwkt_serialize_exit(ifp->if_serializer);
39318892ea20SAggelos Economopoulos 			return EINVAL;
39328892ea20SAggelos Economopoulos 		}
39338892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
39342ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
39358892ea20SAggelos Economopoulos 				err = mxge_open(sc);
39368892ea20SAggelos Economopoulos 			} else {
39378892ea20SAggelos Economopoulos 				/* take care of promis can allmulti
39388892ea20SAggelos Economopoulos 				   flag chages */
39398892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
39408892ea20SAggelos Economopoulos 						    ifp->if_flags & IFF_PROMISC);
39418892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
39428892ea20SAggelos Economopoulos 			}
39438892ea20SAggelos Economopoulos 		} else {
39442ab1b8a9SAggelos Economopoulos 			if (ifp->if_flags & IFF_RUNNING) {
39458892ea20SAggelos Economopoulos 				mxge_close(sc);
39468892ea20SAggelos Economopoulos 			}
39478892ea20SAggelos Economopoulos 		}
39482e8181d0SAggelos Economopoulos 		lwkt_serialize_exit(ifp->if_serializer);
39498892ea20SAggelos Economopoulos 		break;
39508892ea20SAggelos Economopoulos 
39518892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
39528892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
39532e8181d0SAggelos Economopoulos 		lwkt_serialize_enter(sc->ifp->if_serializer);
39548892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
39552e8181d0SAggelos Economopoulos 		lwkt_serialize_exit(sc->ifp->if_serializer);
39568892ea20SAggelos Economopoulos 		break;
39578892ea20SAggelos Economopoulos 
39588892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
39592e8181d0SAggelos Economopoulos 		lwkt_serialize_enter(sc->ifp->if_serializer);
39608892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
39618892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
39628892ea20SAggelos Economopoulos 			if (IFCAP_TXCSUM & ifp->if_capenable) {
39638892ea20SAggelos Economopoulos 				ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4);
39648892ea20SAggelos Economopoulos 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP
39658892ea20SAggelos Economopoulos 						      | CSUM_TSO);
39668892ea20SAggelos Economopoulos 			} else {
39678892ea20SAggelos Economopoulos 				ifp->if_capenable |= IFCAP_TXCSUM;
39688892ea20SAggelos Economopoulos 				ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP);
39698892ea20SAggelos Economopoulos 			}
39708892ea20SAggelos Economopoulos 		} else if (mask & IFCAP_RXCSUM) {
39718892ea20SAggelos Economopoulos 			if (IFCAP_RXCSUM & ifp->if_capenable) {
39728892ea20SAggelos Economopoulos 				ifp->if_capenable &= ~IFCAP_RXCSUM;
39738892ea20SAggelos Economopoulos 				sc->csum_flag = 0;
39748892ea20SAggelos Economopoulos 			} else {
39758892ea20SAggelos Economopoulos 				ifp->if_capenable |= IFCAP_RXCSUM;
39768892ea20SAggelos Economopoulos 				sc->csum_flag = 1;
39778892ea20SAggelos Economopoulos 			}
39788892ea20SAggelos Economopoulos 		}
39798892ea20SAggelos Economopoulos 		if (mask & IFCAP_TSO4) {
39808892ea20SAggelos Economopoulos 			if (IFCAP_TSO4 & ifp->if_capenable) {
39818892ea20SAggelos Economopoulos 				ifp->if_capenable &= ~IFCAP_TSO4;
39828892ea20SAggelos Economopoulos 				ifp->if_hwassist &= ~CSUM_TSO;
39838892ea20SAggelos Economopoulos 			} else if (IFCAP_TXCSUM & ifp->if_capenable) {
39848892ea20SAggelos Economopoulos 				ifp->if_capenable |= IFCAP_TSO4;
39858892ea20SAggelos Economopoulos 				ifp->if_hwassist |= CSUM_TSO;
39868892ea20SAggelos Economopoulos 			} else {
39876c348da6SAggelos Economopoulos 				kprintf("mxge requires tx checksum offload"
39888892ea20SAggelos Economopoulos 				       " be enabled to use TSO\n");
39898892ea20SAggelos Economopoulos 				err = EINVAL;
39908892ea20SAggelos Economopoulos 			}
39918892ea20SAggelos Economopoulos 		}
39928892ea20SAggelos Economopoulos 		if (mask & IFCAP_LRO) {
39938892ea20SAggelos Economopoulos 			if (IFCAP_LRO & ifp->if_capenable)
39948892ea20SAggelos Economopoulos 				err = mxge_change_lro_locked(sc, 0);
39958892ea20SAggelos Economopoulos 			else
39968892ea20SAggelos Economopoulos 				err = mxge_change_lro_locked(sc, mxge_lro_cnt);
39978892ea20SAggelos Economopoulos 		}
39988892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
39998892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
40002e8181d0SAggelos Economopoulos 		lwkt_serialize_exit(sc->ifp->if_serializer);
40018892ea20SAggelos Economopoulos 		VLAN_CAPABILITIES(ifp);
40028892ea20SAggelos Economopoulos 
40038892ea20SAggelos Economopoulos 		break;
40048892ea20SAggelos Economopoulos 
40058892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
40068892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
40078892ea20SAggelos Economopoulos 				    &sc->media, command);
40088892ea20SAggelos Economopoulos                 break;
40098892ea20SAggelos Economopoulos 
40108892ea20SAggelos Economopoulos 	default:
40118892ea20SAggelos Economopoulos 		err = ENOTTY;
40128892ea20SAggelos Economopoulos         }
40138892ea20SAggelos Economopoulos 	return err;
40148892ea20SAggelos Economopoulos }
40158892ea20SAggelos Economopoulos 
40168892ea20SAggelos Economopoulos static void
40178892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
40188892ea20SAggelos Economopoulos {
40198892ea20SAggelos Economopoulos 
40208892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices);
40218892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled",
40228892ea20SAggelos Economopoulos 			  &mxge_flow_control);
40238892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay",
40248892ea20SAggelos Economopoulos 			  &mxge_intr_coal_delay);
40258892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable",
40268892ea20SAggelos Economopoulos 			  &mxge_nvidia_ecrc_enable);
40278892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.force_firmware",
40288892ea20SAggelos Economopoulos 			  &mxge_force_firmware);
40298892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.deassert_wait",
40308892ea20SAggelos Economopoulos 			  &mxge_deassert_wait);
40318892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.verbose",
40328892ea20SAggelos Economopoulos 			  &mxge_verbose);
40338892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks);
40348892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt);
40358892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc);
40368892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type);
40378892ea20SAggelos Economopoulos 	TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu);
40388892ea20SAggelos Economopoulos 	if (sc->lro_cnt != 0)
40398892ea20SAggelos Economopoulos 		mxge_lro_cnt = sc->lro_cnt;
40408892ea20SAggelos Economopoulos 
40418892ea20SAggelos Economopoulos 	if (bootverbose)
40428892ea20SAggelos Economopoulos 		mxge_verbose = 1;
40438892ea20SAggelos Economopoulos 	if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000)
40448892ea20SAggelos Economopoulos 		mxge_intr_coal_delay = 30;
40458892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
40468892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
40478892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
40488892ea20SAggelos Economopoulos 	if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4
40498892ea20SAggelos Economopoulos 	    || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) {
40508892ea20SAggelos Economopoulos 		mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_PORT;
40518892ea20SAggelos Economopoulos 	}
40528892ea20SAggelos Economopoulos 	if (mxge_initial_mtu > ETHERMTU_JUMBO ||
40538892ea20SAggelos Economopoulos 	    mxge_initial_mtu < ETHER_MIN_LEN)
40548892ea20SAggelos Economopoulos 		mxge_initial_mtu = ETHERMTU_JUMBO;
40558892ea20SAggelos Economopoulos }
40568892ea20SAggelos Economopoulos 
40578892ea20SAggelos Economopoulos 
40588892ea20SAggelos Economopoulos static void
40598892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
40608892ea20SAggelos Economopoulos {
40618892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
40628892ea20SAggelos Economopoulos 	int i;
40638892ea20SAggelos Economopoulos 
40648892ea20SAggelos Economopoulos 
40658892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
40668892ea20SAggelos Economopoulos 		return;
40678892ea20SAggelos Economopoulos 
40688892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40698892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
40708892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
40718892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
40728892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
40738892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
40748892ea20SAggelos Economopoulos 			if (ss->tx.br != NULL) {
40758892ea20SAggelos Economopoulos 				drbr_free(ss->tx.br, M_DEVBUF);
40768892ea20SAggelos Economopoulos 				ss->tx.br = NULL;
40778892ea20SAggelos Economopoulos 			}
40788892ea20SAggelos Economopoulos #endif
40798892ea20SAggelos Economopoulos 		}
40808892ea20SAggelos Economopoulos 		if (ss->rx_done.entry != NULL) {
40818892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->rx_done.dma);
40828892ea20SAggelos Economopoulos 			ss->rx_done.entry = NULL;
40838892ea20SAggelos Economopoulos 		}
40848892ea20SAggelos Economopoulos 	}
40856c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
40868892ea20SAggelos Economopoulos 	sc->ss = NULL;
40878892ea20SAggelos Economopoulos }
40888892ea20SAggelos Economopoulos 
40898892ea20SAggelos Economopoulos static int
40908892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
40918892ea20SAggelos Economopoulos {
40928892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
40938892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
40948892ea20SAggelos Economopoulos 	size_t bytes;
40958892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
40968892ea20SAggelos Economopoulos 
40978892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40988892ea20SAggelos Economopoulos 	if (err != 0) {
40998892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
41008892ea20SAggelos Economopoulos 		return err;
41018892ea20SAggelos Economopoulos 	}
41028892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
41038892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
41048892ea20SAggelos Economopoulos 
41058892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->ss) * sc->num_slices;
4106d777b84fSAggelos Economopoulos 	sc->ss = kmalloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO);
41078892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
41088892ea20SAggelos Economopoulos 		return (ENOMEM);
41098892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
41108892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
41118892ea20SAggelos Economopoulos 
41128892ea20SAggelos Economopoulos 		ss->sc = sc;
41138892ea20SAggelos Economopoulos 
41148892ea20SAggelos Economopoulos 		/* allocate per-slice rx interrupt queues */
41158892ea20SAggelos Economopoulos 
41168892ea20SAggelos Economopoulos 		bytes = max_intr_slots * sizeof (*ss->rx_done.entry);
41178892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
41188892ea20SAggelos Economopoulos 		if (err != 0)
41198892ea20SAggelos Economopoulos 			goto abort;
41208892ea20SAggelos Economopoulos 		ss->rx_done.entry = ss->rx_done.dma.addr;
41218892ea20SAggelos Economopoulos 		bzero(ss->rx_done.entry, bytes);
41228892ea20SAggelos Economopoulos 
41238892ea20SAggelos Economopoulos 		/*
41248892ea20SAggelos Economopoulos 		 * allocate the per-slice firmware stats; stats
41258892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
41268892ea20SAggelos Economopoulos 		 * slice for now
41278892ea20SAggelos Economopoulos 		 */
41288892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
41298892ea20SAggelos Economopoulos 		if (i > 0)
41308892ea20SAggelos Economopoulos 			continue;
41318892ea20SAggelos Economopoulos #endif
41328892ea20SAggelos Economopoulos 
41338892ea20SAggelos Economopoulos 		bytes = sizeof (*ss->fw_stats);
41348892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
41358892ea20SAggelos Economopoulos 				     sizeof (*ss->fw_stats), 64);
41368892ea20SAggelos Economopoulos 		if (err != 0)
41378892ea20SAggelos Economopoulos 			goto abort;
41388892ea20SAggelos Economopoulos 		ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr;
41398892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
41408892ea20SAggelos Economopoulos 		ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK,
4141e8a47a7fSAggelos Economopoulos 					   &ss->tx.lock);
41428892ea20SAggelos Economopoulos #endif
41438892ea20SAggelos Economopoulos 	}
41448892ea20SAggelos Economopoulos 
41458892ea20SAggelos Economopoulos 	return (0);
41468892ea20SAggelos Economopoulos 
41478892ea20SAggelos Economopoulos abort:
41488892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
41498892ea20SAggelos Economopoulos 	return (ENOMEM);
41508892ea20SAggelos Economopoulos }
41518892ea20SAggelos Economopoulos 
41528892ea20SAggelos Economopoulos static void
41538892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
41548892ea20SAggelos Economopoulos {
41558892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
41568892ea20SAggelos Economopoulos 	char *old_fw;
41578892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
41588892ea20SAggelos Economopoulos 
41598892ea20SAggelos Economopoulos 	sc->num_slices = 1;
41608892ea20SAggelos Economopoulos 	/*
41618892ea20SAggelos Economopoulos 	 *  don't enable multiple slices if they are not enabled,
41628892ea20SAggelos Economopoulos 	 *  or if this is not an SMP system
41638892ea20SAggelos Economopoulos 	 */
41648892ea20SAggelos Economopoulos 
4165b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
41668892ea20SAggelos Economopoulos 		return;
41678892ea20SAggelos Economopoulos 
41688892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
41698892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
41708892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
41718892ea20SAggelos Economopoulos 		return;
41728892ea20SAggelos Economopoulos 
41738892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
41748892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
41758892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
41768892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
41778892ea20SAggelos Economopoulos 	else
41788892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
41798892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
41808892ea20SAggelos Economopoulos 	if (status != 0) {
41818892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
41828892ea20SAggelos Economopoulos 		return;
41838892ea20SAggelos Economopoulos 	}
41848892ea20SAggelos Economopoulos 
41858892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
41868892ea20SAggelos Economopoulos 	   is alive */
41878892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
41888892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
41898892ea20SAggelos Economopoulos 	if (status != 0) {
41908892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
41918892ea20SAggelos Economopoulos 		goto abort_with_fw;
41928892ea20SAggelos Economopoulos 	}
41938892ea20SAggelos Economopoulos 
41948892ea20SAggelos Economopoulos 	/* get rx ring size */
41958892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
41968892ea20SAggelos Economopoulos 	if (status != 0) {
41978892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
41988892ea20SAggelos Economopoulos 		goto abort_with_fw;
41998892ea20SAggelos Economopoulos 	}
42008892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
42018892ea20SAggelos Economopoulos 
42028892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
42038892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
42048892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
42058892ea20SAggelos Economopoulos 	if (status != 0) {
42068892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
42078892ea20SAggelos Economopoulos 		goto abort_with_fw;
42088892ea20SAggelos Economopoulos 	}
42098892ea20SAggelos Economopoulos 
42108892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
42118892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
42128892ea20SAggelos Economopoulos 	if (status != 0) {
42138892ea20SAggelos Economopoulos 		device_printf(sc->dev,
42148892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
42158892ea20SAggelos Economopoulos 		goto abort_with_fw;
42168892ea20SAggelos Economopoulos 	}
42178892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
42188892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
42198892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
42208892ea20SAggelos Economopoulos 
42218892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
42228892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
4223ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
4224ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
42258892ea20SAggelos Economopoulos 	} else {
42268892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
42278892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
42288892ea20SAggelos Economopoulos 	}
42298892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
42308892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
42318892ea20SAggelos Economopoulos 		sc->num_slices--;
42328892ea20SAggelos Economopoulos 
42338892ea20SAggelos Economopoulos 	if (mxge_verbose)
42348892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
42358892ea20SAggelos Economopoulos 			      sc->num_slices);
42368892ea20SAggelos Economopoulos 
42378892ea20SAggelos Economopoulos 	return;
42388892ea20SAggelos Economopoulos 
42398892ea20SAggelos Economopoulos abort_with_fw:
42408892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
42418892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
42428892ea20SAggelos Economopoulos }
42438892ea20SAggelos Economopoulos 
42448892ea20SAggelos Economopoulos static int
42458892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
42468892ea20SAggelos Economopoulos {
42478892ea20SAggelos Economopoulos 	size_t bytes;
42488892ea20SAggelos Economopoulos 	int count, err, i, rid;
42498892ea20SAggelos Economopoulos 
42508892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
42518892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
42528892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
42538892ea20SAggelos Economopoulos 
42548892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
42558892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
42568892ea20SAggelos Economopoulos 		return ENXIO;
42578892ea20SAggelos Economopoulos 	}
42588892ea20SAggelos Economopoulos 
42598892ea20SAggelos Economopoulos 	count = sc->num_slices;
42608892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
42618892ea20SAggelos Economopoulos 	if (err != 0) {
42628892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
42638892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
42648892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
42658892ea20SAggelos Economopoulos 	}
42668892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
42678892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
42688892ea20SAggelos Economopoulos 			      count, sc->num_slices);
42698892ea20SAggelos Economopoulos 		device_printf(sc->dev,
42708892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
42718892ea20SAggelos Economopoulos 			      count);
42728892ea20SAggelos Economopoulos 		err = ENOSPC;
42738892ea20SAggelos Economopoulos 		goto abort_with_msix;
42748892ea20SAggelos Economopoulos 	}
42758892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
4276d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42778892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
42788892ea20SAggelos Economopoulos 		err = ENOMEM;
42798892ea20SAggelos Economopoulos 		goto abort_with_msix;
42808892ea20SAggelos Economopoulos 	}
42818892ea20SAggelos Economopoulos 
42828892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
42838892ea20SAggelos Economopoulos 		rid = i + 1;
42848892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
42858892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
42868892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
42878892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
42888892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
42898892ea20SAggelos Economopoulos 				      " for message %d\n", i);
42908892ea20SAggelos Economopoulos 			err = ENXIO;
42918892ea20SAggelos Economopoulos 			goto abort_with_res;
42928892ea20SAggelos Economopoulos 		}
42938892ea20SAggelos Economopoulos 	}
42948892ea20SAggelos Economopoulos 
42958892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
4296d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
42978892ea20SAggelos Economopoulos 
42988892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
42998892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
43007d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
43017d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
43022e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
43038892ea20SAggelos Economopoulos 		if (err != 0) {
43048892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
43058892ea20SAggelos Economopoulos 				      "message %d\n", i);
43068892ea20SAggelos Economopoulos 			goto abort_with_intr;
43078892ea20SAggelos Economopoulos 		}
43088892ea20SAggelos Economopoulos 	}
43098892ea20SAggelos Economopoulos 
43108892ea20SAggelos Economopoulos 	if (mxge_verbose) {
43118892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
43128892ea20SAggelos Economopoulos 			      sc->num_slices);
43138892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
43146c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
43156c348da6SAggelos Economopoulos 		kprintf("\n");
43168892ea20SAggelos Economopoulos 	}
43178892ea20SAggelos Economopoulos 	return (0);
43188892ea20SAggelos Economopoulos 
43198892ea20SAggelos Economopoulos abort_with_intr:
43208892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
43218892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
43228892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43238892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
43248892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
43258892ea20SAggelos Economopoulos 		}
43268892ea20SAggelos Economopoulos 	}
4327d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
43288892ea20SAggelos Economopoulos 
43298892ea20SAggelos Economopoulos 
43308892ea20SAggelos Economopoulos abort_with_res:
43318892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
43328892ea20SAggelos Economopoulos 		rid = i + 1;
43338892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
43348892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
43358892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
43368892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
43378892ea20SAggelos Economopoulos 	}
4338d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
43398892ea20SAggelos Economopoulos 
43408892ea20SAggelos Economopoulos 
43418892ea20SAggelos Economopoulos abort_with_msix:
43428892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
43438892ea20SAggelos Economopoulos 
43448892ea20SAggelos Economopoulos abort_with_msix_table:
43458892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
43468892ea20SAggelos Economopoulos 			     sc->msix_table_res);
43478892ea20SAggelos Economopoulos 
43488892ea20SAggelos Economopoulos 	return err;
43498892ea20SAggelos Economopoulos }
43508892ea20SAggelos Economopoulos 
43518892ea20SAggelos Economopoulos static int
43528892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
43538892ea20SAggelos Economopoulos {
43548892ea20SAggelos Economopoulos 	int count, err, rid;
43558892ea20SAggelos Economopoulos 
43568892ea20SAggelos Economopoulos 	count = pci_msi_count(sc->dev);
43578892ea20SAggelos Economopoulos 	if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) {
43588892ea20SAggelos Economopoulos 		rid = 1;
43598892ea20SAggelos Economopoulos 	} else {
43608892ea20SAggelos Economopoulos 		rid = 0;
43618892ea20SAggelos Economopoulos 		sc->legacy_irq = 1;
43628892ea20SAggelos Economopoulos 	}
43638892ea20SAggelos Economopoulos 	sc->irq_res = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &rid, 0, ~0,
43648892ea20SAggelos Economopoulos 					 1, RF_SHAREABLE | RF_ACTIVE);
43658892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
43668892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
43678892ea20SAggelos Economopoulos 		return ENXIO;
43688892ea20SAggelos Economopoulos 	}
43698892ea20SAggelos Economopoulos 	if (mxge_verbose)
43708892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %s irq %ld\n",
43718892ea20SAggelos Economopoulos 			      sc->legacy_irq ? "INTx" : "MSI",
43728892ea20SAggelos Economopoulos 			      rman_get_start(sc->irq_res));
43738892ea20SAggelos Economopoulos 	err = bus_setup_intr(sc->dev, sc->irq_res,
43747d8771d4SAggelos Economopoulos 			     INTR_MPSAFE,
43757d8771d4SAggelos Economopoulos 			     mxge_intr, &sc->ss[0], &sc->ih,
43762e8181d0SAggelos Economopoulos 			     sc->ifp->if_serializer);
43778892ea20SAggelos Economopoulos 	if (err != 0) {
43788892ea20SAggelos Economopoulos 		bus_release_resource(sc->dev, SYS_RES_IRQ,
43798892ea20SAggelos Economopoulos 				     sc->legacy_irq ? 0 : 1, sc->irq_res);
43808892ea20SAggelos Economopoulos 		if (!sc->legacy_irq)
43818892ea20SAggelos Economopoulos 			pci_release_msi(sc->dev);
43828892ea20SAggelos Economopoulos 	}
43838892ea20SAggelos Economopoulos 	return err;
43848892ea20SAggelos Economopoulos }
43858892ea20SAggelos Economopoulos 
43868892ea20SAggelos Economopoulos static void
43878892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
43888892ea20SAggelos Economopoulos {
43898892ea20SAggelos Economopoulos 	int i, rid;
43908892ea20SAggelos Economopoulos 
43918892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
43928892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
43938892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
43948892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
43958892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
43968892ea20SAggelos Economopoulos 		}
43978892ea20SAggelos Economopoulos 	}
4398d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
43998892ea20SAggelos Economopoulos 
44008892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
44018892ea20SAggelos Economopoulos 		rid = i + 1;
44028892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
44038892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
44048892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
44058892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
44068892ea20SAggelos Economopoulos 	}
4407d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
44088892ea20SAggelos Economopoulos 
44098892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
44108892ea20SAggelos Economopoulos 			     sc->msix_table_res);
44118892ea20SAggelos Economopoulos 
44128892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
44138892ea20SAggelos Economopoulos 	return;
44148892ea20SAggelos Economopoulos }
44158892ea20SAggelos Economopoulos 
44168892ea20SAggelos Economopoulos static void
44178892ea20SAggelos Economopoulos mxge_rem_single_irq(mxge_softc_t *sc)
44188892ea20SAggelos Economopoulos {
44198892ea20SAggelos Economopoulos 	bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
44208892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_IRQ,
44218892ea20SAggelos Economopoulos 			     sc->legacy_irq ? 0 : 1, sc->irq_res);
44228892ea20SAggelos Economopoulos 	if (!sc->legacy_irq)
44238892ea20SAggelos Economopoulos 		pci_release_msi(sc->dev);
44248892ea20SAggelos Economopoulos }
44258892ea20SAggelos Economopoulos 
44268892ea20SAggelos Economopoulos static void
44278892ea20SAggelos Economopoulos mxge_rem_irq(mxge_softc_t *sc)
44288892ea20SAggelos Economopoulos {
44298892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
44308892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
44318892ea20SAggelos Economopoulos 	else
44328892ea20SAggelos Economopoulos 		mxge_rem_single_irq(sc);
44338892ea20SAggelos Economopoulos }
44348892ea20SAggelos Economopoulos 
44358892ea20SAggelos Economopoulos static int
44368892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
44378892ea20SAggelos Economopoulos {
44388892ea20SAggelos Economopoulos 	int err;
44398892ea20SAggelos Economopoulos 
44408892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
44418892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
44428892ea20SAggelos Economopoulos 	else
44438892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
44448892ea20SAggelos Economopoulos 
44458892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
44468892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
44478892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
44488892ea20SAggelos Economopoulos 	}
44498892ea20SAggelos Economopoulos 	return err;
44508892ea20SAggelos Economopoulos }
44518892ea20SAggelos Economopoulos 
44528892ea20SAggelos Economopoulos 
44538892ea20SAggelos Economopoulos static int
44548892ea20SAggelos Economopoulos mxge_attach(device_t dev)
44558892ea20SAggelos Economopoulos {
44568892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4457137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
44588892ea20SAggelos Economopoulos 	int err, rid;
44598892ea20SAggelos Economopoulos 
4460f0115d64SAggelos Economopoulos 	/*
4461f0115d64SAggelos Economopoulos 	 * avoid rewriting half the lines in this file to use
4462f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4463f0115d64SAggelos Economopoulos 	 */
4464f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
44658892ea20SAggelos Economopoulos 	sc->dev = dev;
44668892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
44678892ea20SAggelos Economopoulos 
44688892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
44698892ea20SAggelos Economopoulos 				 1,			/* alignment */
44708892ea20SAggelos Economopoulos 				 0,			/* boundary */
44718892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
44728892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
44738892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
44748892ea20SAggelos Economopoulos 				 65536 + 256,		/* maxsize */
44758892ea20SAggelos Economopoulos 				 MXGE_MAX_SEND_DESC, 	/* num segs */
44768892ea20SAggelos Economopoulos 				 65536,			/* maxsegsize */
44778892ea20SAggelos Economopoulos 				 0,			/* flags */
44788892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
44798892ea20SAggelos Economopoulos 
44808892ea20SAggelos Economopoulos 	if (err != 0) {
44818892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating parent dmat\n",
44828892ea20SAggelos Economopoulos 			      err);
44838892ea20SAggelos Economopoulos 		goto abort_with_nothing;
44848892ea20SAggelos Economopoulos 	}
44858892ea20SAggelos Economopoulos 
4486137195a6SAggelos Economopoulos 	sc->ifp = ifp;
44878892ea20SAggelos Economopoulos 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
44888892ea20SAggelos Economopoulos 
4489*e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
44908892ea20SAggelos Economopoulos 
44918892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
44928892ea20SAggelos Economopoulos 
44938892ea20SAggelos Economopoulos 	/* Map the board into the kernel */
44948892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
44958892ea20SAggelos Economopoulos 	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0,
44968892ea20SAggelos Economopoulos 					 ~0, 1, RF_ACTIVE);
44978892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
44988892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
44998892ea20SAggelos Economopoulos 		err = ENXIO;
45002e8181d0SAggelos Economopoulos 		goto abort_with_nothing;
45018892ea20SAggelos Economopoulos 	}
45028892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
45038892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
45048892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
45058892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
45068892ea20SAggelos Economopoulos 			      rman_get_size(sc->mem_res));
45078892ea20SAggelos Economopoulos 		err = ENXIO;
45088892ea20SAggelos Economopoulos 		goto abort_with_mem_res;
45098892ea20SAggelos Economopoulos 	}
45108892ea20SAggelos Economopoulos 
45118892ea20SAggelos Economopoulos 	/* make NULL terminated copy of the EEPROM strings section of
45128892ea20SAggelos Economopoulos 	   lanai SRAM */
45138892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
45148892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
45158892ea20SAggelos Economopoulos 				rman_get_bushandle(sc->mem_res),
45168892ea20SAggelos Economopoulos 				sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
45178892ea20SAggelos Economopoulos 				sc->eeprom_strings,
45188892ea20SAggelos Economopoulos 				MXGE_EEPROM_STRINGS_SIZE - 2);
45198892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
45208892ea20SAggelos Economopoulos 	if (err != 0)
45218892ea20SAggelos Economopoulos 		goto abort_with_mem_res;
45228892ea20SAggelos Economopoulos 
45238892ea20SAggelos Economopoulos 	/* Enable write combining for efficient use of PCIe bus */
45248892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
45258892ea20SAggelos Economopoulos 
45268892ea20SAggelos Economopoulos 	/* Allocate the out of band dma memory */
45278892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->cmd_dma,
45288892ea20SAggelos Economopoulos 			     sizeof (mxge_cmd_t), 64);
45298892ea20SAggelos Economopoulos 	if (err != 0)
45308892ea20SAggelos Economopoulos 		goto abort_with_mem_res;
45318892ea20SAggelos Economopoulos 	sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr;
45328892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
45338892ea20SAggelos Economopoulos 	if (err != 0)
45348892ea20SAggelos Economopoulos 		goto abort_with_cmd_dma;
45358892ea20SAggelos Economopoulos 
45368892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
45378892ea20SAggelos Economopoulos 	if (err != 0)
45388892ea20SAggelos Economopoulos 		goto abort_with_zeropad_dma;
45398892ea20SAggelos Economopoulos 
45408892ea20SAggelos Economopoulos 	/* select & load the firmware */
45418892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
45428892ea20SAggelos Economopoulos 	if (err != 0)
45438892ea20SAggelos Economopoulos 		goto abort_with_dmabench;
45448892ea20SAggelos Economopoulos 	sc->intr_coal_delay = mxge_intr_coal_delay;
45458892ea20SAggelos Economopoulos 
45468892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
45478892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
45488892ea20SAggelos Economopoulos 	if (err != 0)
45498892ea20SAggelos Economopoulos 		goto abort_with_dmabench;
45508892ea20SAggelos Economopoulos 
45518892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
45528892ea20SAggelos Economopoulos 	if (err != 0)
45538892ea20SAggelos Economopoulos 		goto abort_with_slices;
45548892ea20SAggelos Economopoulos 
45558892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
45568892ea20SAggelos Economopoulos 	if (err != 0) {
45578892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to allocate rings\n");
45588892ea20SAggelos Economopoulos 		goto abort_with_dmabench;
45598892ea20SAggelos Economopoulos 	}
45608892ea20SAggelos Economopoulos 
45618892ea20SAggelos Economopoulos 	err = mxge_add_irq(sc);
45628892ea20SAggelos Economopoulos 	if (err != 0) {
45638892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to add irq\n");
45648892ea20SAggelos Economopoulos 		goto abort_with_rings;
45658892ea20SAggelos Economopoulos 	}
45668892ea20SAggelos Economopoulos 
45678892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
45688892ea20SAggelos Economopoulos 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 |
45698892ea20SAggelos Economopoulos 		IFCAP_VLAN_MTU;
45708892ea20SAggelos Economopoulos #ifdef INET
45718892ea20SAggelos Economopoulos 	ifp->if_capabilities |= IFCAP_LRO;
45728892ea20SAggelos Economopoulos #endif
45738892ea20SAggelos Economopoulos 
45748892ea20SAggelos Economopoulos #ifdef MXGE_NEW_VLAN_API
45758892ea20SAggelos Economopoulos 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
45768892ea20SAggelos Economopoulos #endif
45778892ea20SAggelos Economopoulos 
45788892ea20SAggelos Economopoulos 	sc->max_mtu = mxge_max_mtu(sc);
45798892ea20SAggelos Economopoulos 	if (sc->max_mtu >= 9000)
45808892ea20SAggelos Economopoulos 		ifp->if_capabilities |= IFCAP_JUMBO_MTU;
45818892ea20SAggelos Economopoulos 	else
45828892ea20SAggelos Economopoulos 		device_printf(dev, "MTU limited to %d.  Install "
45838892ea20SAggelos Economopoulos 			      "latest firmware for 9000 byte jumbo support\n",
45848892ea20SAggelos Economopoulos 			      sc->max_mtu - ETHER_HDR_LEN);
45858892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
45868892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
45878892ea20SAggelos Economopoulos 	if (sc->lro_cnt == 0)
45888892ea20SAggelos Economopoulos 		ifp->if_capenable &= ~IFCAP_LRO;
45898892ea20SAggelos Economopoulos 	sc->csum_flag = 1;
45908892ea20SAggelos Economopoulos         ifp->if_init = mxge_init;
45918892ea20SAggelos Economopoulos         ifp->if_softc = sc;
45928892ea20SAggelos Economopoulos         ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
45938892ea20SAggelos Economopoulos         ifp->if_ioctl = mxge_ioctl;
45948892ea20SAggelos Economopoulos         ifp->if_start = mxge_start;
45958892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
45968892ea20SAggelos Economopoulos 	ifmedia_init(&sc->media, 0, mxge_media_change,
45978892ea20SAggelos Economopoulos 		     mxge_media_status);
45988892ea20SAggelos Economopoulos 	mxge_set_media(sc, IFM_ETHER | IFM_AUTO);
45998892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
46008892ea20SAggelos Economopoulos 	sc->dying = 0;
4601cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
46028892ea20SAggelos Economopoulos 	/* ether_ifattach sets mtu to ETHERMTU */
46038892ea20SAggelos Economopoulos 	if (mxge_initial_mtu != ETHERMTU)
46048892ea20SAggelos Economopoulos 		mxge_change_mtu(sc, mxge_initial_mtu);
46058892ea20SAggelos Economopoulos 
46068892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
46078892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
46088892ea20SAggelos Economopoulos 	ifp->if_transmit = mxge_transmit;
46098892ea20SAggelos Economopoulos 	ifp->if_qflush = mxge_qflush;
46108892ea20SAggelos Economopoulos #endif
46118892ea20SAggelos Economopoulos 	return 0;
46128892ea20SAggelos Economopoulos 
46138892ea20SAggelos Economopoulos abort_with_rings:
46148892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
46158892ea20SAggelos Economopoulos abort_with_slices:
46168892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
46178892ea20SAggelos Economopoulos abort_with_dmabench:
46188892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->dmabench_dma);
46198892ea20SAggelos Economopoulos abort_with_zeropad_dma:
46208892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->zeropad_dma);
46218892ea20SAggelos Economopoulos abort_with_cmd_dma:
46228892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->cmd_dma);
46238892ea20SAggelos Economopoulos abort_with_mem_res:
46248892ea20SAggelos Economopoulos 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
46258892ea20SAggelos Economopoulos 	pci_disable_busmaster(dev);
46268892ea20SAggelos Economopoulos 	bus_dma_tag_destroy(sc->parent_dmat);
46278892ea20SAggelos Economopoulos abort_with_nothing:
46288892ea20SAggelos Economopoulos 	return err;
46298892ea20SAggelos Economopoulos }
46308892ea20SAggelos Economopoulos 
46318892ea20SAggelos Economopoulos static int
46328892ea20SAggelos Economopoulos mxge_detach(device_t dev)
46338892ea20SAggelos Economopoulos {
46348892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
46358892ea20SAggelos Economopoulos 
46362e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
46378892ea20SAggelos Economopoulos 	sc->dying = 1;
46382ab1b8a9SAggelos Economopoulos 	if (sc->ifp->if_flags & IFF_RUNNING)
46398892ea20SAggelos Economopoulos 		mxge_close(sc);
4640*e3dc37faSAggelos Economopoulos 	/*
4641*e3dc37faSAggelos Economopoulos 	 * XXX: race: the callout callback could be spinning on
4642*e3dc37faSAggelos Economopoulos 	 * the serializer and run anyway
4643*e3dc37faSAggelos Economopoulos 	 */
4644*e3dc37faSAggelos Economopoulos 	callout_stop(&sc->co_hdl);
46452e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
4646*e3dc37faSAggelos Economopoulos 
46478892ea20SAggelos Economopoulos 	ether_ifdetach(sc->ifp);
46488892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
46498892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 0);
46508892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
46518892ea20SAggelos Economopoulos 	mxge_rem_irq(sc);
46528892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
46538892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
46548892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->dmabench_dma);
46558892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->zeropad_dma);
46568892ea20SAggelos Economopoulos 	mxge_dma_free(&sc->cmd_dma);
46578892ea20SAggelos Economopoulos 	bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res);
46588892ea20SAggelos Economopoulos 	pci_disable_busmaster(dev);
46598892ea20SAggelos Economopoulos 	bus_dma_tag_destroy(sc->parent_dmat);
46608892ea20SAggelos Economopoulos 	return 0;
46618892ea20SAggelos Economopoulos }
46628892ea20SAggelos Economopoulos 
46638892ea20SAggelos Economopoulos static int
46648892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
46658892ea20SAggelos Economopoulos {
46668892ea20SAggelos Economopoulos 	return 0;
46678892ea20SAggelos Economopoulos }
46688892ea20SAggelos Economopoulos 
46698892ea20SAggelos Economopoulos /*
46708892ea20SAggelos Economopoulos   This file uses Myri10GE driver indentation.
46718892ea20SAggelos Economopoulos 
46728892ea20SAggelos Economopoulos   Local Variables:
46738892ea20SAggelos Economopoulos   c-file-style:"linux"
46748892ea20SAggelos Economopoulos   tab-width:8
46758892ea20SAggelos Economopoulos   End:
46768892ea20SAggelos Economopoulos */
4677