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