xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision 5ca32f31)
18892ea20SAggelos Economopoulos /******************************************************************************
28892ea20SAggelos Economopoulos 
389d55360SSepherosa Ziehau Copyright (c) 2006-2013, 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 
2889d55360SSepherosa Ziehau $FreeBSD: head/sys/dev/mxge/if_mxge.c 254263 2013-08-12 23:30:01Z scottl $
298892ea20SAggelos Economopoulos 
3032af04f7SSascha Wildner ***************************************************************************/
318892ea20SAggelos Economopoulos 
3289d55360SSepherosa Ziehau #include "opt_inet.h"
3389d55360SSepherosa Ziehau 
348892ea20SAggelos Economopoulos #include <sys/param.h>
358892ea20SAggelos Economopoulos #include <sys/systm.h>
368892ea20SAggelos Economopoulos #include <sys/linker.h>
378892ea20SAggelos Economopoulos #include <sys/firmware.h>
388892ea20SAggelos Economopoulos #include <sys/endian.h>
3905e71c89SAggelos Economopoulos #include <sys/in_cksum.h>
408892ea20SAggelos Economopoulos #include <sys/sockio.h>
418892ea20SAggelos Economopoulos #include <sys/mbuf.h>
428892ea20SAggelos Economopoulos #include <sys/malloc.h>
438892ea20SAggelos Economopoulos #include <sys/kernel.h>
448892ea20SAggelos Economopoulos #include <sys/module.h>
452e8181d0SAggelos Economopoulos #include <sys/serialize.h>
468892ea20SAggelos Economopoulos #include <sys/socket.h>
478892ea20SAggelos Economopoulos #include <sys/sysctl.h>
488892ea20SAggelos Economopoulos 
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 
7789d55360SSepherosa Ziehau #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>
83b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/if_mxge_var.h>
848892ea20SAggelos Economopoulos 
858892ea20SAggelos Economopoulos /* tunable params */
868892ea20SAggelos Economopoulos static int mxge_nvidia_ecrc_enable = 1;
878892ea20SAggelos Economopoulos static int mxge_force_firmware = 0;
887cc92483SSepherosa Ziehau static int mxge_intr_coal_delay = MXGE_INTR_COAL_DELAY;
898892ea20SAggelos Economopoulos static int mxge_deassert_wait = 1;
908892ea20SAggelos Economopoulos static int mxge_flow_control = 1;
918892ea20SAggelos Economopoulos static int mxge_ticks;
928892ea20SAggelos Economopoulos static int mxge_max_slices = 1;
938892ea20SAggelos Economopoulos static int mxge_always_promisc = 0;
9489d55360SSepherosa Ziehau static int mxge_throttle = 0;
957cc92483SSepherosa Ziehau static int mxge_msi_enable = 1;
967cc92483SSepherosa Ziehau 
97c7431c78SSepherosa Ziehau static const char *mxge_fw_unaligned = "mxge_ethp_z8e";
98c7431c78SSepherosa Ziehau static const char *mxge_fw_aligned = "mxge_eth_z8e";
99c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
100c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
1018892ea20SAggelos Economopoulos 
1027cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.max_slices", &mxge_max_slices);
1037cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.flow_control_enabled", &mxge_flow_control);
1047cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.intr_coal_delay", &mxge_intr_coal_delay);
1057cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.nvidia_ecrc_enable", &mxge_nvidia_ecrc_enable);
1067cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.force_firmware", &mxge_force_firmware);
1077cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.deassert_wait", &mxge_deassert_wait);
1087cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.ticks", &mxge_ticks);
1097cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.always_promisc", &mxge_always_promisc);
1107cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.throttle", &mxge_throttle);
1117cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.msi.enable", &mxge_msi_enable);
1127cc92483SSepherosa Ziehau 
1138892ea20SAggelos Economopoulos static int mxge_probe(device_t dev);
1148892ea20SAggelos Economopoulos static int mxge_attach(device_t dev);
1158892ea20SAggelos Economopoulos static int mxge_detach(device_t dev);
1168892ea20SAggelos Economopoulos static int mxge_shutdown(device_t dev);
1178892ea20SAggelos Economopoulos static void mxge_intr(void *arg);
1188892ea20SAggelos Economopoulos 
11989d55360SSepherosa Ziehau static device_method_t mxge_methods[] = {
1208892ea20SAggelos Economopoulos 	/* Device interface */
1218892ea20SAggelos Economopoulos 	DEVMETHOD(device_probe, mxge_probe),
1228892ea20SAggelos Economopoulos 	DEVMETHOD(device_attach, mxge_attach),
1238892ea20SAggelos Economopoulos 	DEVMETHOD(device_detach, mxge_detach),
1248892ea20SAggelos Economopoulos 	DEVMETHOD(device_shutdown, mxge_shutdown),
125d3c9c58eSSascha Wildner 	DEVMETHOD_END
1268892ea20SAggelos Economopoulos };
1278892ea20SAggelos Economopoulos 
12889d55360SSepherosa Ziehau static driver_t mxge_driver = {
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.*/
137aa2b9d05SSascha Wildner DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, NULL, NULL);
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);
1432c29ffc6SSepherosa Ziehau static void mxge_close(mxge_softc_t *sc, int down);
1448892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1458892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
1468892ea20SAggelos Economopoulos 
1478892ea20SAggelos Economopoulos static int
1488892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1498892ea20SAggelos Economopoulos {
15024e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
15124e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
15224e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
15324e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1548892ea20SAggelos Economopoulos 
1558892ea20SAggelos Economopoulos 		switch (rev) {
1568892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1578892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1588892ea20SAggelos Economopoulos 			break;
1598892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1608892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1618892ea20SAggelos Economopoulos 			break;
1628892ea20SAggelos Economopoulos 		default:
1638892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
16424e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1658892ea20SAggelos Economopoulos 			break;
1668892ea20SAggelos Economopoulos 		}
1678892ea20SAggelos Economopoulos 		return 0;
1688892ea20SAggelos Economopoulos 	}
1698892ea20SAggelos Economopoulos 	return ENXIO;
1708892ea20SAggelos Economopoulos }
1718892ea20SAggelos Economopoulos 
1728892ea20SAggelos Economopoulos static void
1738892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1748892ea20SAggelos Economopoulos {
17589d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
1768892ea20SAggelos Economopoulos 	vm_offset_t len;
1778892ea20SAggelos Economopoulos 
1788892ea20SAggelos Economopoulos 	sc->wc = 1;
1798892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
18089d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
18189d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
1829eb279beSAggelos Economopoulos #endif
1838892ea20SAggelos Economopoulos }
1848892ea20SAggelos Economopoulos 
1858892ea20SAggelos Economopoulos static int
1867cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
1878892ea20SAggelos Economopoulos     bus_size_t alignment)
1888892ea20SAggelos Economopoulos {
1897cc92483SSepherosa Ziehau 	bus_size_t boundary;
1908892ea20SAggelos Economopoulos 	int err;
1918892ea20SAggelos Economopoulos 
1927cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
1938892ea20SAggelos Economopoulos 		boundary = 0;
1947cc92483SSepherosa Ziehau 	else
1958892ea20SAggelos Economopoulos 		boundary = 4096;
1968892ea20SAggelos Economopoulos 
1977cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
1987cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
1997cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2008892ea20SAggelos Economopoulos 	if (err != 0) {
2017cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2028892ea20SAggelos Economopoulos 		return err;
2038892ea20SAggelos Economopoulos 	}
2048892ea20SAggelos Economopoulos 	return 0;
2058892ea20SAggelos Economopoulos }
2068892ea20SAggelos Economopoulos 
2078892ea20SAggelos Economopoulos static void
2087cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2098892ea20SAggelos Economopoulos {
2107cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2117cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2127cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2138892ea20SAggelos Economopoulos }
2148892ea20SAggelos Economopoulos 
2158892ea20SAggelos Economopoulos /*
2168892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2178892ea20SAggelos Economopoulos  * SN=x\0
2188892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2198892ea20SAggelos Economopoulos  * PC=text\0
2208892ea20SAggelos Economopoulos  */
2218892ea20SAggelos Economopoulos static int
2228892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2238892ea20SAggelos Economopoulos {
224c7431c78SSepherosa Ziehau 	const char *ptr;
22589d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
22689d55360SSepherosa Ziehau 	char *endptr;
2278892ea20SAggelos Economopoulos 
2288892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2298892ea20SAggelos Economopoulos 	found_mac = 0;
23089d55360SSepherosa Ziehau 	found_sn2 = 0;
23189d55360SSepherosa Ziehau 	while (*ptr != '\0') {
23289d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
23389d55360SSepherosa Ziehau 			ptr += 4;
23489d55360SSepherosa Ziehau 			for (i = 0;;) {
23589d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
23689d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2378892ea20SAggelos Economopoulos 					goto abort;
23889d55360SSepherosa Ziehau 				ptr = endptr;
23989d55360SSepherosa Ziehau 				if (++i == 6)
24089d55360SSepherosa Ziehau 					break;
24189d55360SSepherosa Ziehau 				if (*ptr++ != ':')
24289d55360SSepherosa Ziehau 					goto abort;
24389d55360SSepherosa Ziehau 			}
2448892ea20SAggelos Economopoulos 			found_mac = 1;
24589d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2468892ea20SAggelos Economopoulos 			ptr += 3;
24789d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
24889d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
24989d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2508892ea20SAggelos Economopoulos 			ptr += 3;
25189d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25289d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
25389d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
25489d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
25589d55360SSepherosa Ziehau 			ptr += 4;
25689d55360SSepherosa Ziehau 			found_sn2 = 1;
25789d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25889d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2598892ea20SAggelos Economopoulos 		}
26089d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2618892ea20SAggelos Economopoulos 	}
2628892ea20SAggelos Economopoulos 
2638892ea20SAggelos Economopoulos 	if (found_mac)
2648892ea20SAggelos Economopoulos 		return 0;
2658892ea20SAggelos Economopoulos 
2668892ea20SAggelos Economopoulos abort:
2678892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2688892ea20SAggelos Economopoulos 	return ENXIO;
2698892ea20SAggelos Economopoulos }
2708892ea20SAggelos Economopoulos 
27189d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
27289d55360SSepherosa Ziehau 
2738892ea20SAggelos Economopoulos static void
2748892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
2758892ea20SAggelos Economopoulos {
2768892ea20SAggelos Economopoulos 	uint32_t val;
2778892ea20SAggelos Economopoulos 	unsigned long base, off;
2788892ea20SAggelos Economopoulos 	char *va, *cfgptr;
2798892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
2808892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
2818892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
2828892ea20SAggelos Economopoulos 	uint32_t *ptr32;
2838892ea20SAggelos Economopoulos 
2848892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
2858892ea20SAggelos Economopoulos 		return;
2868892ea20SAggelos Economopoulos 
2878892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
2888892ea20SAggelos Economopoulos 	if (pdev == NULL) {
2898892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
2908892ea20SAggelos Economopoulos 		return;
2918892ea20SAggelos Economopoulos 	}
2928892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
2938892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
2948892ea20SAggelos Economopoulos 
2958892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
2968892ea20SAggelos Economopoulos 		return;
2978892ea20SAggelos Economopoulos 
2988892ea20SAggelos Economopoulos 	base = 0;
2998892ea20SAggelos Economopoulos 
3008892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3018892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3028892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3038892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3048892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3058892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3068892ea20SAggelos Economopoulos 		if (mcp55 &&
3078892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3088892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3098892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3108892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3118892ea20SAggelos Economopoulos 		}
3128892ea20SAggelos Economopoulos 	}
3138892ea20SAggelos Economopoulos 	if (!base)
3148892ea20SAggelos Economopoulos 		return;
3158892ea20SAggelos Economopoulos 
3167cc92483SSepherosa Ziehau 	/*
3177cc92483SSepherosa Ziehau 	 * XXXX
3187cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3197cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3207cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3217cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3227cc92483SSepherosa Ziehau 	 * FreeBSD grows support for extended pcie config space access
3238892ea20SAggelos Economopoulos 	 */
3248892ea20SAggelos Economopoulos #if 0
3257cc92483SSepherosa Ziehau 	/*
3267cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3277cc92483SSepherosa Ziehau 	 * config space
3287cc92483SSepherosa Ziehau 	 */
3298892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3308892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3318892ea20SAggelos Economopoulos 		val |= 0x40;
3328892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3338892ea20SAggelos Economopoulos 		return;
3348892ea20SAggelos Economopoulos 	}
3358892ea20SAggelos Economopoulos #endif
3367cc92483SSepherosa Ziehau 	/*
3377cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3388892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3398892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3408892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3418892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3428892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3438892ea20SAggelos Economopoulos 	 */
3448892ea20SAggelos Economopoulos 
3458892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3468892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3478892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3488892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3498892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3508892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3518892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3528892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3538892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3548892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3558892ea20SAggelos Economopoulos 
3567cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3577cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3588892ea20SAggelos Economopoulos 
3598892ea20SAggelos Economopoulos 	/* map it into the kernel */
3608892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3618892ea20SAggelos Economopoulos 	if (va == NULL) {
3628892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3638892ea20SAggelos Economopoulos 		return;
3648892ea20SAggelos Economopoulos 	}
3658892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3668892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3678892ea20SAggelos Economopoulos 
3688892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3698892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3708892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3718892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3728892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3738892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3748892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3758892ea20SAggelos Economopoulos 		return;
3768892ea20SAggelos Economopoulos 	}
3778892ea20SAggelos Economopoulos 
3788892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
3798892ea20SAggelos Economopoulos 	val = *ptr32;
3808892ea20SAggelos Economopoulos 
3818892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
3828892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
3838892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3848892ea20SAggelos Economopoulos 		return;
3858892ea20SAggelos Economopoulos 	}
3868892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
3878892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3887cc92483SSepherosa Ziehau 	if (bootverbose) {
3897cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
3907cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
3918892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
3927cc92483SSepherosa Ziehau 	}
3938892ea20SAggelos Economopoulos }
39489d55360SSepherosa Ziehau 
39589d55360SSepherosa Ziehau #else	/* __i386__ || __x86_64__ */
39689d55360SSepherosa Ziehau 
3978892ea20SAggelos Economopoulos static void
3988892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
3998892ea20SAggelos Economopoulos {
4007cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4018892ea20SAggelos Economopoulos }
4028892ea20SAggelos Economopoulos 
40389d55360SSepherosa Ziehau #endif
4048892ea20SAggelos Economopoulos 
4058892ea20SAggelos Economopoulos static int
4068892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4078892ea20SAggelos Economopoulos {
4088892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4097cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4108892ea20SAggelos Economopoulos 	int status;
4118892ea20SAggelos Economopoulos 	uint32_t len;
4127cc92483SSepherosa Ziehau 	const char *test = " ";
4138892ea20SAggelos Economopoulos 
4147cc92483SSepherosa Ziehau 	/*
4157cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4168892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4178892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4188892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4198892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4208892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4218892ea20SAggelos Economopoulos 	 * transfers took to complete.
4228892ea20SAggelos Economopoulos 	 */
4238892ea20SAggelos Economopoulos 
4248892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4258892ea20SAggelos Economopoulos 
4268892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4278892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4288892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4298892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4308892ea20SAggelos Economopoulos 	if (status != 0) {
4318892ea20SAggelos Economopoulos 		test = "read";
4328892ea20SAggelos Economopoulos 		goto abort;
4338892ea20SAggelos Economopoulos 	}
4347cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4357cc92483SSepherosa Ziehau 
4368892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4378892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4388892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4398892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4408892ea20SAggelos Economopoulos 	if (status != 0) {
4418892ea20SAggelos Economopoulos 		test = "write";
4428892ea20SAggelos Economopoulos 		goto abort;
4438892ea20SAggelos Economopoulos 	}
4447cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4458892ea20SAggelos Economopoulos 
4468892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4478892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4488892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4498892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4508892ea20SAggelos Economopoulos 	if (status != 0) {
4518892ea20SAggelos Economopoulos 		test = "read/write";
4528892ea20SAggelos Economopoulos 		goto abort;
4538892ea20SAggelos Economopoulos 	}
4548892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4558892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4568892ea20SAggelos Economopoulos 
4578892ea20SAggelos Economopoulos abort:
4587cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4598892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4608892ea20SAggelos Economopoulos 		    test, status);
4617cc92483SSepherosa Ziehau 	}
4628892ea20SAggelos Economopoulos 	return status;
4638892ea20SAggelos Economopoulos }
4648892ea20SAggelos Economopoulos 
4658892ea20SAggelos Economopoulos /*
4668892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4678892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4688892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4698892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4708892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4718892ea20SAggelos Economopoulos  *
4728892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4738892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4748892ea20SAggelos Economopoulos  *
4758892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
4768892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
4778892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
4788892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
4798892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
4808892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
4818892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
4828892ea20SAggelos Economopoulos  */
4838892ea20SAggelos Economopoulos static int
4848892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
4858892ea20SAggelos Economopoulos {
4868892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
4878892ea20SAggelos Economopoulos 	int reg, status;
4888892ea20SAggelos Economopoulos 	uint16_t pectl;
4898892ea20SAggelos Economopoulos 
4908892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
4917cc92483SSepherosa Ziehau 
4928892ea20SAggelos Economopoulos 	/*
4938892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
4948892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
4958892ea20SAggelos Economopoulos 	 */
4968892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
4978892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
4988892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
4997cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5008892ea20SAggelos Economopoulos 			    pectl);
5018892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5028892ea20SAggelos Economopoulos 		}
5038892ea20SAggelos Economopoulos 	}
5048892ea20SAggelos Economopoulos 
5058892ea20SAggelos Economopoulos 	/*
5067cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5078892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5088892ea20SAggelos Economopoulos 	 */
5098892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5108892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5117cc92483SSepherosa Ziehau 	if (status != 0)
5128892ea20SAggelos Economopoulos 		return status;
5138892ea20SAggelos Economopoulos 
5148892ea20SAggelos Economopoulos 	/*
5158892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5168892ea20SAggelos Economopoulos 	 */
5178892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5188892ea20SAggelos Economopoulos 
5198892ea20SAggelos Economopoulos 	/*
5208892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
52189d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5228892ea20SAggelos Economopoulos 	 */
52389d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
52489d55360SSepherosa Ziehau 		return 0;
5258892ea20SAggelos Economopoulos 
5268892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5278892ea20SAggelos Economopoulos 	if (status == 0)
5288892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5298892ea20SAggelos Economopoulos 
5308892ea20SAggelos Economopoulos 	if (status != E2BIG)
5318892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5327cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5338892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5348892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5357cc92483SSepherosa Ziehau 	}
5368892ea20SAggelos Economopoulos 	return status;
5378892ea20SAggelos Economopoulos }
5388892ea20SAggelos Economopoulos 
5398892ea20SAggelos Economopoulos static int
5408892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5418892ea20SAggelos Economopoulos {
5428892ea20SAggelos Economopoulos 	int aligned = 0;
54389d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5448892ea20SAggelos Economopoulos 
54589d55360SSepherosa Ziehau 	if (sc->throttle)
54689d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5478892ea20SAggelos Economopoulos 
54889d55360SSepherosa Ziehau 	if (force_firmware != 0) {
54989d55360SSepherosa Ziehau 		if (force_firmware == 1)
5508892ea20SAggelos Economopoulos 			aligned = 1;
5518892ea20SAggelos Economopoulos 		else
5528892ea20SAggelos Economopoulos 			aligned = 0;
5537cc92483SSepherosa Ziehau 		if (bootverbose) {
5548892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5558892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5568892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5577cc92483SSepherosa Ziehau 		}
5588892ea20SAggelos Economopoulos 		goto abort;
5598892ea20SAggelos Economopoulos 	}
5608892ea20SAggelos Economopoulos 
5617cc92483SSepherosa Ziehau 	/*
5627cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5637cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5647cc92483SSepherosa Ziehau 	 */
5658892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5667cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5677cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5688892ea20SAggelos Economopoulos 		aligned = 1;
5698892ea20SAggelos Economopoulos 		goto abort;
5708892ea20SAggelos Economopoulos 	}
5718892ea20SAggelos Economopoulos 
5727cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5738892ea20SAggelos Economopoulos 		return 0;
5748892ea20SAggelos Economopoulos 
5758892ea20SAggelos Economopoulos abort:
5768892ea20SAggelos Economopoulos 	if (aligned) {
5778892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
5788892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
5798892ea20SAggelos Economopoulos 	} else {
5808892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
5818892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
5828892ea20SAggelos Economopoulos 	}
5837cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
5848892ea20SAggelos Economopoulos }
5858892ea20SAggelos Economopoulos 
5868892ea20SAggelos Economopoulos static int
5878892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
5888892ea20SAggelos Economopoulos {
5898892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
5908892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Bad firmware type: 0x%x\n",
5918892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
5928892ea20SAggelos Economopoulos 		return EIO;
5938892ea20SAggelos Economopoulos 	}
5948892ea20SAggelos Economopoulos 
5957cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
5967cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
5977cc92483SSepherosa Ziehau 	if (bootverbose)
5988892ea20SAggelos Economopoulos 		device_printf(sc->dev, "firmware id: %s\n", hdr->version);
5998892ea20SAggelos Economopoulos 
600b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6018892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6028892ea20SAggelos Economopoulos 
6037cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6047cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6058892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Found firmware version %s\n",
6068892ea20SAggelos Economopoulos 		    sc->fw_version);
6078892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Driver needs %d.%d\n",
6088892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6098892ea20SAggelos Economopoulos 		return EINVAL;
6108892ea20SAggelos Economopoulos 	}
6118892ea20SAggelos Economopoulos 	return 0;
6128892ea20SAggelos Economopoulos }
6138892ea20SAggelos Economopoulos 
6148892ea20SAggelos Economopoulos static void *
6158892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6168892ea20SAggelos Economopoulos {
6177cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6188892ea20SAggelos Economopoulos }
6198892ea20SAggelos Economopoulos 
6208892ea20SAggelos Economopoulos static void
6218892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6228892ea20SAggelos Economopoulos {
623d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6248892ea20SAggelos Economopoulos }
625d83c779aSSascha Wildner 
6268892ea20SAggelos Economopoulos static int
6278892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6288892ea20SAggelos Economopoulos {
629d83c779aSSascha Wildner 	z_stream zs;
630d83c779aSSascha Wildner 	char *inflate_buffer;
631d83c779aSSascha Wildner 	const struct firmware *fw;
6328892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6338892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6348892ea20SAggelos Economopoulos 	int status;
6358892ea20SAggelos Economopoulos 	unsigned int i;
63689d55360SSepherosa Ziehau 	char dummy;
6378892ea20SAggelos Economopoulos 	size_t fw_len;
6388892ea20SAggelos Economopoulos 
639d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6408892ea20SAggelos Economopoulos 	if (fw == NULL) {
6418892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Could not find firmware image %s\n",
6428892ea20SAggelos Economopoulos 		    sc->fw_name);
6438892ea20SAggelos Economopoulos 		return ENOENT;
6448892ea20SAggelos Economopoulos 	}
645d83c779aSSascha Wildner 
6467cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6478892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6488892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6498892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6508892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6518892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6528892ea20SAggelos Economopoulos 		status = EIO;
6538892ea20SAggelos Economopoulos 		goto abort_with_fw;
6548892ea20SAggelos Economopoulos 	}
6558892ea20SAggelos Economopoulos 
6567cc92483SSepherosa Ziehau 	/*
6577cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6587cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6597cc92483SSepherosa Ziehau 	 */
6608892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6617cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6628892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6638892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6648892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6658892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6668892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6678892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6688892ea20SAggelos Economopoulos 		device_printf(sc->dev, "zlib %d\n", status);
6698892ea20SAggelos Economopoulos 		status = EIO;
6708892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6718892ea20SAggelos Economopoulos 	}
672d83c779aSSascha Wildner 
6737cc92483SSepherosa Ziehau 	/* Check id */
6747cc92483SSepherosa Ziehau 	hdr_offset =
6757cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
6768892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
6778892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Bad firmware file");
6788892ea20SAggelos Economopoulos 		status = EIO;
679d83c779aSSascha Wildner 		goto abort_with_buffer;
6808892ea20SAggelos Economopoulos 	}
681d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
6828892ea20SAggelos Economopoulos 
6838892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
6848892ea20SAggelos Economopoulos 	if (status != 0)
685d83c779aSSascha Wildner 		goto abort_with_buffer;
6868892ea20SAggelos Economopoulos 
6878892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
6888892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
6897cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
6908892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
6918892ea20SAggelos Economopoulos 		wmb();
69289d55360SSepherosa Ziehau 		dummy = *sc->sram;
6938892ea20SAggelos Economopoulos 		wmb();
6948892ea20SAggelos Economopoulos 	}
6958892ea20SAggelos Economopoulos 
6968892ea20SAggelos Economopoulos 	*limit = fw_len;
6978892ea20SAggelos Economopoulos 	status = 0;
6988892ea20SAggelos Economopoulos abort_with_buffer:
699d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7008892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7018892ea20SAggelos Economopoulos abort_with_fw:
702d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7038892ea20SAggelos Economopoulos 	return status;
7048892ea20SAggelos Economopoulos }
7058892ea20SAggelos Economopoulos 
7068892ea20SAggelos Economopoulos /*
7078892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7088892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7098892ea20SAggelos Economopoulos  */
7108892ea20SAggelos Economopoulos static void
7118892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7128892ea20SAggelos Economopoulos {
7138892ea20SAggelos Economopoulos 	char buf_bytes[72];
7148892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7158892ea20SAggelos Economopoulos 	volatile char *submit;
7168892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7178892ea20SAggelos Economopoulos 	int i;
7188892ea20SAggelos Economopoulos 
7198892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7208892ea20SAggelos Economopoulos 
7217cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7228892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7238892ea20SAggelos Economopoulos 	*confirm = 0;
7248892ea20SAggelos Economopoulos 	wmb();
7258892ea20SAggelos Economopoulos 
7267cc92483SSepherosa Ziehau 	/*
7277cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7287cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7297cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7308892ea20SAggelos Economopoulos 	 */
7317cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7327cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7338892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7348892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7358892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7367cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7377cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7388892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7398892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7408892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7418892ea20SAggelos Economopoulos 
7428892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7438892ea20SAggelos Economopoulos 
7448892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7458892ea20SAggelos Economopoulos 	wmb();
7468892ea20SAggelos Economopoulos 	DELAY(1000);
7478892ea20SAggelos Economopoulos 	wmb();
7488892ea20SAggelos Economopoulos 	i = 0;
7498892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7508892ea20SAggelos Economopoulos 		DELAY(1000);
7518892ea20SAggelos Economopoulos 		i++;
7528892ea20SAggelos Economopoulos 	}
7538892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7546ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7557cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7568892ea20SAggelos Economopoulos 	}
7578892ea20SAggelos Economopoulos }
7588892ea20SAggelos Economopoulos 
7598892ea20SAggelos Economopoulos static int
7608892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7618892ea20SAggelos Economopoulos {
7628892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7638892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7648892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7658892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7668892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7678892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7688892ea20SAggelos Economopoulos 
7696ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7708892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7718892ea20SAggelos Economopoulos 
7728892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7738892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7748892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
7758892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
7767cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7777cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7788892ea20SAggelos Economopoulos 
7798892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
7808892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
7812e8181d0SAggelos Economopoulos 
7828892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
7838892ea20SAggelos Economopoulos 	wmb();
7848892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
7858892ea20SAggelos Economopoulos 
7866ee6cba3SSepherosa Ziehau 	/*
7876ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
7886ee6cba3SSepherosa Ziehau 	 */
7898892ea20SAggelos Economopoulos 	err = EAGAIN;
7908892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
7918892ea20SAggelos Economopoulos 		wmb();
7928892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
7938892ea20SAggelos Economopoulos 		case 0:
7948892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
7958892ea20SAggelos Economopoulos 			err = 0;
7968892ea20SAggelos Economopoulos 			break;
7978892ea20SAggelos Economopoulos 		case 0xffffffff:
7988892ea20SAggelos Economopoulos 			DELAY(1000);
7998892ea20SAggelos Economopoulos 			break;
8008892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8018892ea20SAggelos Economopoulos 			err = ENOSYS;
8028892ea20SAggelos Economopoulos 			break;
8038892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8048892ea20SAggelos Economopoulos 			err = E2BIG;
8058892ea20SAggelos Economopoulos 			break;
8068892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8078892ea20SAggelos Economopoulos 			err = EBUSY;
8088892ea20SAggelos Economopoulos 			break;
80989d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
81089d55360SSepherosa Ziehau 			err = ENXIO;
81189d55360SSepherosa Ziehau 			break;
8128892ea20SAggelos Economopoulos 		default:
8136ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8148892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8158892ea20SAggelos Economopoulos 			err = ENXIO;
8168892ea20SAggelos Economopoulos 			break;
8178892ea20SAggelos Economopoulos 		}
8188892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8198892ea20SAggelos Economopoulos 			break;
8208892ea20SAggelos Economopoulos 	}
8216ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8226ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8238892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8246ee6cba3SSepherosa Ziehau 	}
8258892ea20SAggelos Economopoulos 	return err;
8268892ea20SAggelos Economopoulos }
8278892ea20SAggelos Economopoulos 
8288892ea20SAggelos Economopoulos static int
8298892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8308892ea20SAggelos Economopoulos {
8318892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8328892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8338892ea20SAggelos Economopoulos 	size_t hdr_offset;
8348892ea20SAggelos Economopoulos 	int status;
8358892ea20SAggelos Economopoulos 
8367cc92483SSepherosa Ziehau 	/*
8377cc92483SSepherosa Ziehau 	 * Find running firmware header
8387cc92483SSepherosa Ziehau 	 */
8397cc92483SSepherosa Ziehau 	hdr_offset =
8407cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8418892ea20SAggelos Economopoulos 
8428892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8438892ea20SAggelos Economopoulos 		device_printf(sc->dev,
8447cc92483SSepherosa Ziehau 		    "Running firmware has bad header offset (%zu)\n",
8457cc92483SSepherosa Ziehau 		    hdr_offset);
8468892ea20SAggelos Economopoulos 		return EIO;
8478892ea20SAggelos Economopoulos 	}
8488892ea20SAggelos Economopoulos 
8497cc92483SSepherosa Ziehau 	/*
8507cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8517cc92483SSepherosa Ziehau 	 * validate firmware
8527cc92483SSepherosa Ziehau 	 */
8537cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8548892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8557cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8568892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
857d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8588892ea20SAggelos Economopoulos 
8598892ea20SAggelos Economopoulos 	/*
8607cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8618892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8628892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8638892ea20SAggelos Economopoulos 	 */
8648892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8658892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8668892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8678892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Adopting fw %d.%d.%d: "
8688892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8697cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8708892ea20SAggelos Economopoulos 	}
8718892ea20SAggelos Economopoulos 
8728892ea20SAggelos Economopoulos 	return status;
8738892ea20SAggelos Economopoulos }
8748892ea20SAggelos Economopoulos 
8758892ea20SAggelos Economopoulos static int
8768892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
8778892ea20SAggelos Economopoulos {
8788892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
8798892ea20SAggelos Economopoulos 	volatile char *submit;
8808892ea20SAggelos Economopoulos 	char buf_bytes[72];
8818892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
8828892ea20SAggelos Economopoulos 	int status, i;
8838892ea20SAggelos Economopoulos 
8848892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
8858892ea20SAggelos Economopoulos 
8868892ea20SAggelos Economopoulos 	size = sc->sram_size;
8878892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
8888892ea20SAggelos Economopoulos 	if (status) {
8898892ea20SAggelos Economopoulos 		if (!adopt)
8908892ea20SAggelos Economopoulos 			return status;
8917cc92483SSepherosa Ziehau 
8927cc92483SSepherosa Ziehau 		/*
8937cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
8947cc92483SSepherosa Ziehau 		 * it is new enough
8957cc92483SSepherosa Ziehau 		 */
8968892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
8978892ea20SAggelos Economopoulos 		if (status) {
8988892ea20SAggelos Economopoulos 			device_printf(sc->dev,
8998892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
9008892ea20SAggelos Economopoulos 			return status;
9018892ea20SAggelos Economopoulos 		}
9028892ea20SAggelos Economopoulos 		device_printf(sc->dev,
9038892ea20SAggelos Economopoulos 		    "Successfully adopted running firmware\n");
9047cc92483SSepherosa Ziehau 
9058892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9068892ea20SAggelos Economopoulos 			device_printf(sc->dev,
9077cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9087cc92483SSepherosa Ziehau 			     "For optimal\n");
9098892ea20SAggelos Economopoulos 			device_printf(sc->dev,
9107cc92483SSepherosa Ziehau 			     "performance consider loading "
9117cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9128892ea20SAggelos Economopoulos 		}
9138892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9148892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9158892ea20SAggelos Economopoulos 		return 0;
9168892ea20SAggelos Economopoulos 	}
9177cc92483SSepherosa Ziehau 
9187cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9198892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9208892ea20SAggelos Economopoulos 	*confirm = 0;
9218892ea20SAggelos Economopoulos 	wmb();
9227cc92483SSepherosa Ziehau 
9237cc92483SSepherosa Ziehau 	/*
9247cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9257cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9267cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9278892ea20SAggelos Economopoulos 	 */
9288892ea20SAggelos Economopoulos 
9297cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9307cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9318892ea20SAggelos Economopoulos 
9328892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9338892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9348892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9358892ea20SAggelos Economopoulos 
9367cc92483SSepherosa Ziehau 	/*
9377cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9387cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9397cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9408892ea20SAggelos Economopoulos 	 */
9418892ea20SAggelos Economopoulos 					/* where the code starts*/
9428892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9438892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9448892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9458892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9468892ea20SAggelos Economopoulos 
9478892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9488892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9498892ea20SAggelos Economopoulos 	wmb();
9508892ea20SAggelos Economopoulos 	DELAY(1000);
9518892ea20SAggelos Economopoulos 	wmb();
9528892ea20SAggelos Economopoulos 	i = 0;
9538892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9548892ea20SAggelos Economopoulos 		DELAY(1000*10);
9558892ea20SAggelos Economopoulos 		i++;
9568892ea20SAggelos Economopoulos 	}
9578892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9588892ea20SAggelos Economopoulos 		device_printf(sc->dev,"handoff failed (%p = 0x%x)",
9598892ea20SAggelos Economopoulos 		    confirm, *confirm);
9608892ea20SAggelos Economopoulos 		return ENXIO;
9618892ea20SAggelos Economopoulos 	}
9628892ea20SAggelos Economopoulos 	return 0;
9638892ea20SAggelos Economopoulos }
9648892ea20SAggelos Economopoulos 
9658892ea20SAggelos Economopoulos static int
9668892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9678892ea20SAggelos Economopoulos {
9688892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9698892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9708892ea20SAggelos Economopoulos 
9717cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9727cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9737cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9747cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9758892ea20SAggelos Economopoulos }
9768892ea20SAggelos Economopoulos 
9778892ea20SAggelos Economopoulos static int
9788892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
9798892ea20SAggelos Economopoulos {
9808892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9818892ea20SAggelos Economopoulos 	int status;
9828892ea20SAggelos Economopoulos 
9838892ea20SAggelos Economopoulos 	if (pause)
9847cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
9858892ea20SAggelos Economopoulos 	else
9867cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
9878892ea20SAggelos Economopoulos 	if (status) {
9888892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed to set flow control mode\n");
9898892ea20SAggelos Economopoulos 		return ENXIO;
9908892ea20SAggelos Economopoulos 	}
9918892ea20SAggelos Economopoulos 	sc->pause = pause;
9928892ea20SAggelos Economopoulos 	return 0;
9938892ea20SAggelos Economopoulos }
9948892ea20SAggelos Economopoulos 
9958892ea20SAggelos Economopoulos static void
9968892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
9978892ea20SAggelos Economopoulos {
9988892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9998892ea20SAggelos Economopoulos 	int status;
10008892ea20SAggelos Economopoulos 
10018892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10028892ea20SAggelos Economopoulos 		promisc = 1;
10038892ea20SAggelos Economopoulos 
10048892ea20SAggelos Economopoulos 	if (promisc)
10057cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10068892ea20SAggelos Economopoulos 	else
10077cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10087cc92483SSepherosa Ziehau 	if (status)
10098892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed to set promisc mode\n");
10108892ea20SAggelos Economopoulos }
10118892ea20SAggelos Economopoulos 
10128892ea20SAggelos Economopoulos static void
10138892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10148892ea20SAggelos Economopoulos {
10158892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10168892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10178892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10188892ea20SAggelos Economopoulos 	int err;
10198892ea20SAggelos Economopoulos 
10208892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10218892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10228892ea20SAggelos Economopoulos 		return;
10238892ea20SAggelos Economopoulos 
10248892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
10258892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10268892ea20SAggelos Economopoulos 	if (err != 0) {
10278892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI,"
10288892ea20SAggelos Economopoulos 		    " error status: %d\n", err);
10298892ea20SAggelos Economopoulos 		return;
10308892ea20SAggelos Economopoulos 	}
10318892ea20SAggelos Economopoulos 
10328892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10338892ea20SAggelos Economopoulos 		return;
10348892ea20SAggelos Economopoulos 
10357cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10367cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10378892ea20SAggelos Economopoulos 		return;
10388892ea20SAggelos Economopoulos 	}
10398892ea20SAggelos Economopoulos 
10407cc92483SSepherosa Ziehau 	/* Flush all the filters */
10417cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10427cc92483SSepherosa Ziehau 	if (err != 0) {
10437cc92483SSepherosa Ziehau 		device_printf(sc->dev,
10447cc92483SSepherosa Ziehau 		    "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10457cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10467cc92483SSepherosa Ziehau 		return;
10477cc92483SSepherosa Ziehau 	}
10488892ea20SAggelos Economopoulos 
10497cc92483SSepherosa Ziehau 	/*
10507cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10517cc92483SSepherosa Ziehau 	 */
1052441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10538892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10548892ea20SAggelos Economopoulos 			continue;
10557cc92483SSepherosa Ziehau 
10568892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10578892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10588892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10598892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10608892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10618892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10628892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10638892ea20SAggelos Economopoulos 		if (err != 0) {
10648892ea20SAggelos Economopoulos 			device_printf(sc->dev, "Failed "
10657cc92483SSepherosa Ziehau 			    "MXGEFW_JOIN_MULTICAST_GROUP, "
10667cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10677cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10688892ea20SAggelos Economopoulos 			return;
10698892ea20SAggelos Economopoulos 		}
10708892ea20SAggelos Economopoulos 	}
10717cc92483SSepherosa Ziehau 
10728892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10738892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10748892ea20SAggelos Economopoulos 	if (err != 0) {
10757cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI, "
10767cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10778892ea20SAggelos Economopoulos 	}
10788892ea20SAggelos Economopoulos }
10798892ea20SAggelos Economopoulos 
108089d55360SSepherosa Ziehau #if 0
10818892ea20SAggelos Economopoulos static int
10828892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
10838892ea20SAggelos Economopoulos {
10848892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10858892ea20SAggelos Economopoulos 	int status;
10868892ea20SAggelos Economopoulos 
10878892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
10888892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10898892ea20SAggelos Economopoulos 
10908892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
10918892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
10928892ea20SAggelos Economopoulos 	cmd.data0 = 0;
10938892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
10948892ea20SAggelos Economopoulos 			       &cmd);
10958892ea20SAggelos Economopoulos 	if (status == 0)
10968892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10978892ea20SAggelos Economopoulos 
10988892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
10998892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
11008892ea20SAggelos Economopoulos }
110189d55360SSepherosa Ziehau #endif
11028892ea20SAggelos Economopoulos 
11038892ea20SAggelos Economopoulos static int
11048892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11058892ea20SAggelos Economopoulos {
11068892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11078892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11088892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11098892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11108892ea20SAggelos Economopoulos 	int slice, status;
11118892ea20SAggelos Economopoulos 
11127cc92483SSepherosa Ziehau 	/*
11137cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11147cc92483SSepherosa Ziehau 	 * is alive
11157cc92483SSepherosa Ziehau 	 */
11168892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11178892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11188892ea20SAggelos Economopoulos 	if (status != 0) {
11196ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11208892ea20SAggelos Economopoulos 		return ENXIO;
11218892ea20SAggelos Economopoulos 	}
11228892ea20SAggelos Economopoulos 
11238892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11248892ea20SAggelos Economopoulos 
11257cc92483SSepherosa Ziehau 	/* Set the intrq size */
11268892ea20SAggelos Economopoulos 	cmd.data0 = sc->rx_ring_size;
11278892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11288892ea20SAggelos Economopoulos 
11298892ea20SAggelos Economopoulos 	/*
11308892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11318892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11328892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11338892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11348892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11358892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11368892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11378892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11388892ea20SAggelos Economopoulos 	 */
11398892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11407cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11417cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11428892ea20SAggelos Economopoulos 		if (status != 0) {
11436ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11448892ea20SAggelos Economopoulos 			return status;
11458892ea20SAggelos Economopoulos 		}
11467cc92483SSepherosa Ziehau 
11478892ea20SAggelos Economopoulos 		/*
11488892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11498892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11508892ea20SAggelos Economopoulos 		 */
11518892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11528892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
11538892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
11548892ea20SAggelos Economopoulos 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11558892ea20SAggelos Economopoulos #endif
11567cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11578892ea20SAggelos Economopoulos 		if (status != 0) {
11586ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11598892ea20SAggelos Economopoulos 			return status;
11608892ea20SAggelos Economopoulos 		}
11618892ea20SAggelos Economopoulos 	}
11628892ea20SAggelos Economopoulos 
11638892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11648892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11658892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
11668892ea20SAggelos Economopoulos 			rx_done = &sc->ss[slice].rx_done;
11678892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
11687cc92483SSepherosa Ziehau 			cmd.data0 =
11697cc92483SSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(rx_done->dma.dmem_busaddr);
11707cc92483SSepherosa Ziehau 			cmd.data1 =
11717cc92483SSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(rx_done->dma.dmem_busaddr);
11728892ea20SAggelos Economopoulos 			cmd.data2 = slice;
11737cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
11748892ea20SAggelos Economopoulos 			    &cmd);
11758892ea20SAggelos Economopoulos 		}
11768892ea20SAggelos Economopoulos 	}
11778892ea20SAggelos Economopoulos 
11787cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
11797cc92483SSepherosa Ziehau 	    &cmd);
11808892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11818892ea20SAggelos Economopoulos 
11828892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11838892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11848892ea20SAggelos Economopoulos 
11857cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
11868892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
11877cc92483SSepherosa Ziehau 
11888892ea20SAggelos Economopoulos 	if (status != 0) {
11896ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
11908892ea20SAggelos Economopoulos 		return status;
11918892ea20SAggelos Economopoulos 	}
11928892ea20SAggelos Economopoulos 
11938892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11948892ea20SAggelos Economopoulos 
11957cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
11967cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
11978892ea20SAggelos Economopoulos 
11988892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
11998892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
12008892ea20SAggelos Economopoulos 
12018892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
12027cc92483SSepherosa Ziehau 
12037cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12048892ea20SAggelos Economopoulos 		ss->rx_done.idx = 0;
12058892ea20SAggelos Economopoulos 		ss->rx_done.cnt = 0;
12068892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12078892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12088892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12098892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12108892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12118892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12128892ea20SAggelos Economopoulos 		ss->tx.wake = 0;
12138892ea20SAggelos Economopoulos 		ss->tx.defrag = 0;
12148892ea20SAggelos Economopoulos 		ss->tx.stall = 0;
12158892ea20SAggelos Economopoulos 		ss->rx_big.cnt = 0;
12168892ea20SAggelos Economopoulos 		ss->rx_small.cnt = 0;
12177cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12187cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12198892ea20SAggelos Economopoulos 	}
12208892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12217cc92483SSepherosa Ziehau 
12228892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12238892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12248892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12258892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12267cc92483SSepherosa Ziehau 
122789d55360SSepherosa Ziehau 	if (sc->throttle) {
122889d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12297cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12306ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
123189d55360SSepherosa Ziehau 	}
12328892ea20SAggelos Economopoulos 	return status;
12338892ea20SAggelos Economopoulos }
12348892ea20SAggelos Economopoulos 
12358892ea20SAggelos Economopoulos static int
123689d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
123789d55360SSepherosa Ziehau {
123889d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
123989d55360SSepherosa Ziehau 	mxge_softc_t *sc;
124089d55360SSepherosa Ziehau 	int err;
124189d55360SSepherosa Ziehau 	unsigned int throttle;
124289d55360SSepherosa Ziehau 
124389d55360SSepherosa Ziehau 	sc = arg1;
124489d55360SSepherosa Ziehau 	throttle = sc->throttle;
124589d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
124689d55360SSepherosa Ziehau 	if (err != 0) {
124789d55360SSepherosa Ziehau 		return err;
124889d55360SSepherosa Ziehau 	}
124989d55360SSepherosa Ziehau 
125089d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
125189d55360SSepherosa Ziehau 		return 0;
125289d55360SSepherosa Ziehau 
125389d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
125489d55360SSepherosa Ziehau 		return EINVAL;
125589d55360SSepherosa Ziehau 
125689d55360SSepherosa Ziehau 	/* XXX SERIALIZE */
125789d55360SSepherosa Ziehau 	lwkt_serialize_enter(sc->ifp->if_serializer);
125889d55360SSepherosa Ziehau 
125989d55360SSepherosa Ziehau 	cmd.data0 = throttle;
126089d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
126189d55360SSepherosa Ziehau 	if (err == 0)
126289d55360SSepherosa Ziehau 		sc->throttle = throttle;
126389d55360SSepherosa Ziehau 
126489d55360SSepherosa Ziehau 	lwkt_serialize_exit(sc->ifp->if_serializer);
126589d55360SSepherosa Ziehau 	return err;
126689d55360SSepherosa Ziehau }
126789d55360SSepherosa Ziehau 
126889d55360SSepherosa Ziehau static int
12698892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
12708892ea20SAggelos Economopoulos {
12718892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12728892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
12738892ea20SAggelos Economopoulos 	int err;
12748892ea20SAggelos Economopoulos 
12758892ea20SAggelos Economopoulos 	sc = arg1;
12768892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
12778892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
12788892ea20SAggelos Economopoulos 	if (err != 0) {
12798892ea20SAggelos Economopoulos 		return err;
12808892ea20SAggelos Economopoulos 	}
12818892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
12828892ea20SAggelos Economopoulos 		return 0;
12838892ea20SAggelos Economopoulos 
12848892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
12858892ea20SAggelos Economopoulos 		return EINVAL;
12868892ea20SAggelos Economopoulos 
128789d55360SSepherosa Ziehau 	/* XXX SERIALIZE */
12882e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
128989d55360SSepherosa Ziehau 
12908892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
12918892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
12928892ea20SAggelos Economopoulos 
12932e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
12948892ea20SAggelos Economopoulos 	return err;
12958892ea20SAggelos Economopoulos }
12968892ea20SAggelos Economopoulos 
12978892ea20SAggelos Economopoulos static int
12988892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
12998892ea20SAggelos Economopoulos {
13008892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
13018892ea20SAggelos Economopoulos 	unsigned int enabled;
13028892ea20SAggelos Economopoulos 	int err;
13038892ea20SAggelos Economopoulos 
13048892ea20SAggelos Economopoulos 	sc = arg1;
13058892ea20SAggelos Economopoulos 	enabled = sc->pause;
13068892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
13078892ea20SAggelos Economopoulos 	if (err != 0) {
13088892ea20SAggelos Economopoulos 		return err;
13098892ea20SAggelos Economopoulos 	}
13108892ea20SAggelos Economopoulos 	if (enabled == sc->pause)
13118892ea20SAggelos Economopoulos 		return 0;
13128892ea20SAggelos Economopoulos 
131389d55360SSepherosa Ziehau 	/* XXX SERIALIZE */
13142e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
13158892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
13162e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
13178892ea20SAggelos Economopoulos 
13188892ea20SAggelos Economopoulos 	return err;
13198892ea20SAggelos Economopoulos }
13208892ea20SAggelos Economopoulos 
13218892ea20SAggelos Economopoulos static int
13228892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13238892ea20SAggelos Economopoulos {
13248892ea20SAggelos Economopoulos 	int err;
13258892ea20SAggelos Economopoulos 
13268892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13278892ea20SAggelos Economopoulos 		return EFAULT;
13288892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13298892ea20SAggelos Economopoulos 	arg1 = NULL;
13308892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13318892ea20SAggelos Economopoulos 
13328892ea20SAggelos Economopoulos 	return err;
13338892ea20SAggelos Economopoulos }
13348892ea20SAggelos Economopoulos 
13358892ea20SAggelos Economopoulos static void
13368892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13378892ea20SAggelos Economopoulos {
1338798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13398892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13408892ea20SAggelos Economopoulos 		int slice;
13418892ea20SAggelos Economopoulos 
13428892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13438892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1344798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13458892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13468892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13478892ea20SAggelos Economopoulos 			}
1348798c3369SSepherosa Ziehau 		}
1349798c3369SSepherosa Ziehau 	}
1350798c3369SSepherosa Ziehau 
1351798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13528892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13538892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1354798c3369SSepherosa Ziehau 	}
1355798c3369SSepherosa Ziehau 
1356798c3369SSepherosa Ziehau 	if (sc->sysctl_tree != NULL) {
1357bbac37fbSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1358bbac37fbSAggelos Economopoulos 		sc->sysctl_tree = NULL;
13598892ea20SAggelos Economopoulos 	}
1360798c3369SSepherosa Ziehau }
13618892ea20SAggelos Economopoulos 
13628892ea20SAggelos Economopoulos static void
13638892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13648892ea20SAggelos Economopoulos {
13658892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13668892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13678892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13688892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13698892ea20SAggelos Economopoulos 	int slice;
13708892ea20SAggelos Economopoulos 	char slice_num[8];
13718892ea20SAggelos Economopoulos 
1372b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1373b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1374b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
13757cc92483SSepherosa Ziehau 	    OID_AUTO, device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "");
1376b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1377b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1378b6737651SAggelos Economopoulos 		return;
1379b6737651SAggelos Economopoulos 	}
1380b6737651SAggelos Economopoulos 
1381b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
13828892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13838892ea20SAggelos Economopoulos 
13847cc92483SSepherosa Ziehau 	/*
13857cc92483SSepherosa Ziehau 	 * Random information
13867cc92483SSepherosa Ziehau 	 */
13877cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13887cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
13898892ea20SAggelos Economopoulos 
13907cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
13917cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
13928892ea20SAggelos Economopoulos 
13937cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
13947cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
13958892ea20SAggelos Economopoulos 
13967cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
13977cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
139889d55360SSepherosa Ziehau 
13997cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
14007cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
14018892ea20SAggelos Economopoulos 
14027cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
14037cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
14048892ea20SAggelos Economopoulos 
14057cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
14067cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
14078892ea20SAggelos Economopoulos 
14087cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
14097cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
14108892ea20SAggelos Economopoulos 
14117cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14127cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14137cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14147cc92483SSepherosa Ziehau 
14157cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14167cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14177cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14187cc92483SSepherosa Ziehau 
14197cc92483SSepherosa Ziehau 	/*
14207cc92483SSepherosa Ziehau 	 * Performance related tunables
14217cc92483SSepherosa Ziehau 	 */
14227cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14237cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14247cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14257cc92483SSepherosa Ziehau 
14267cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14277cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14287cc92483SSepherosa Ziehau 	    "Transmit throttling");
14297cc92483SSepherosa Ziehau 
14307cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "flow_control_enabled",
14317cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_flow_control, "I",
14327cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14337cc92483SSepherosa Ziehau 
14347cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14357cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14367cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14377cc92483SSepherosa Ziehau 
14387cc92483SSepherosa Ziehau 	/*
14397cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14407cc92483SSepherosa Ziehau 	 * Need to swap it
14417cc92483SSepherosa Ziehau 	 */
14427cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14437cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14447cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14457cc92483SSepherosa Ziehau 
14467cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14477cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14487cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14497cc92483SSepherosa Ziehau 
14507cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14517cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14527cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14537cc92483SSepherosa Ziehau 
14547cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14557cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14567cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14577cc92483SSepherosa Ziehau 
14587cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14597cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14607cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14617cc92483SSepherosa Ziehau 
14627cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14637cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14647cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14657cc92483SSepherosa Ziehau 
14667cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14677cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14687cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14697cc92483SSepherosa Ziehau 
14707cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14717cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14727cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14737cc92483SSepherosa Ziehau 
14747cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14757cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14767cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14777cc92483SSepherosa Ziehau 
14787cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14797cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14807cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14817cc92483SSepherosa Ziehau 
14827cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14837cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14847cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14857cc92483SSepherosa Ziehau 
14867cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14877cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14887cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
14897cc92483SSepherosa Ziehau 
14907cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
14917cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
14927cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
14938892ea20SAggelos Economopoulos 
14948892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
14958892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
14967cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
14977cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1498798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1499798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1500798c3369SSepherosa Ziehau 		return;
1501798c3369SSepherosa Ziehau 	}
15028892ea20SAggelos Economopoulos 
15038892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
15048892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
15058892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
15068892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
15078892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1508b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
15097cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15107cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1511798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1512798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1513798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1514798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1515798c3369SSepherosa Ziehau 		}
15168892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15177cc92483SSepherosa Ziehau 
15187cc92483SSepherosa Ziehau 		/*
15197cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15207cc92483SSepherosa Ziehau 		 */
15217cc92483SSepherosa Ziehau 
15227cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15237cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_small.cnt, 0, "rx_small_cnt");
15247cc92483SSepherosa Ziehau 
15257cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15267cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_big.cnt, 0, "rx_small_cnt");
15278892ea20SAggelos Economopoulos 
15288892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
15298892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
15308892ea20SAggelos Economopoulos 		if (slice > 0)
15318892ea20SAggelos Economopoulos 			continue;
15328892ea20SAggelos Economopoulos #endif
15338892ea20SAggelos Economopoulos 
15347cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15357cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15367cc92483SSepherosa Ziehau 
15377cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15387cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15397cc92483SSepherosa Ziehau 
15407cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15417cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15427cc92483SSepherosa Ziehau 
15437cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_stall",
15447cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.stall, 0, "tx_stall");
15457cc92483SSepherosa Ziehau 
15467cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_wake",
15477cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.wake, 0, "tx_wake");
15487cc92483SSepherosa Ziehau 
15497cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_defrag",
15507cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.defrag, 0, "tx_defrag");
15517cc92483SSepherosa Ziehau 
15527cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15537cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15547cc92483SSepherosa Ziehau 
15557cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15567cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15577cc92483SSepherosa Ziehau 
15587cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15597cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15608892ea20SAggelos Economopoulos 	}
15618892ea20SAggelos Economopoulos }
15628892ea20SAggelos Economopoulos 
156389d55360SSepherosa Ziehau /*
156489d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
156589d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
156689d55360SSepherosa Ziehau  */
1567ddbf91b7SSepherosa Ziehau static __inline void
15688892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15698892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15708892ea20SAggelos Economopoulos {
15718892ea20SAggelos Economopoulos 	int idx, starting_slot;
1572*5ca32f31SSepherosa Ziehau 
15738892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15748892ea20SAggelos Economopoulos 	while (cnt > 1) {
15758892ea20SAggelos Economopoulos 		cnt--;
15768892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
1577*5ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15788892ea20SAggelos Economopoulos 		wmb();
15798892ea20SAggelos Economopoulos 	}
15808892ea20SAggelos Economopoulos }
15818892ea20SAggelos Economopoulos 
15828892ea20SAggelos Economopoulos /*
158389d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15848892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15858892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15868892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15878892ea20SAggelos Economopoulos  */
1588ddbf91b7SSepherosa Ziehau static __inline void
158989d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15908892ea20SAggelos Economopoulos {
15918892ea20SAggelos Economopoulos 	int idx, i;
15928892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15938892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15948892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15958892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15968892ea20SAggelos Economopoulos 	uint8_t last_flags;
15978892ea20SAggelos Economopoulos 
15988892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15998892ea20SAggelos Economopoulos 
16008892ea20SAggelos Economopoulos 	last_flags = src->flags;
16018892ea20SAggelos Economopoulos 	src->flags = 0;
16028892ea20SAggelos Economopoulos 	wmb();
16038892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
16048892ea20SAggelos Economopoulos 	srcp = src;
16058892ea20SAggelos Economopoulos 
16068892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
1607*5ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
16088892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
16098892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
16108892ea20SAggelos Economopoulos 			srcp += 2;
16118892ea20SAggelos Economopoulos 			dstp += 2;
16128892ea20SAggelos Economopoulos 		}
16138892ea20SAggelos Economopoulos 	} else {
1614*5ca32f31SSepherosa Ziehau 		/*
1615*5ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
1616*5ca32f31SSepherosa Ziehau 		 * that it is submitted below
1617*5ca32f31SSepherosa Ziehau 		 */
16188892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16198892ea20SAggelos Economopoulos 		i = 0;
16208892ea20SAggelos Economopoulos 	}
16218892ea20SAggelos Economopoulos 	if (i < cnt) {
1622*5ca32f31SSepherosa Ziehau 		/* Submit the first request */
16238892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16248892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16258892ea20SAggelos Economopoulos 	}
16268892ea20SAggelos Economopoulos 
1627*5ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16288892ea20SAggelos Economopoulos 	src->flags = last_flags;
16298892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16308892ea20SAggelos Economopoulos 	src_ints+=3;
16318892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16328892ea20SAggelos Economopoulos 	dst_ints+=3;
16338892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16348892ea20SAggelos Economopoulos 	tx->req += cnt;
16358892ea20SAggelos Economopoulos 	wmb();
16368892ea20SAggelos Economopoulos }
16378892ea20SAggelos Economopoulos 
163889d55360SSepherosa Ziehau static int
163989d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
164089d55360SSepherosa Ziehau {
164189d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
164289d55360SSepherosa Ziehau 	struct mbuf *m;
164389d55360SSepherosa Ziehau 
164489d55360SSepherosa Ziehau 	m = *mp;
164589d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
164689d55360SSepherosa Ziehau 
164789d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
164889d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
164989d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
165089d55360SSepherosa Ziehau 
165189d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
165289d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
165389d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
165489d55360SSepherosa Ziehau 
165589d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
165689d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
165789d55360SSepherosa Ziehau 		if (m == NULL) {
165889d55360SSepherosa Ziehau 			*mp = NULL;
165989d55360SSepherosa Ziehau 			return ENOBUFS;
166089d55360SSepherosa Ziehau 		}
166189d55360SSepherosa Ziehau 		*mp = m;
166289d55360SSepherosa Ziehau 	}
166389d55360SSepherosa Ziehau 	return 0;
166489d55360SSepherosa Ziehau }
16658892ea20SAggelos Economopoulos 
16668892ea20SAggelos Economopoulos static void
1667*5ca32f31SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mbuf *m, int busdma_seg_cnt)
16688892ea20SAggelos Economopoulos {
16698892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16708892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16718892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16728892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16738892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16748892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16758892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
16768892ea20SAggelos Economopoulos 
16778892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16788892ea20SAggelos Economopoulos 
1679*5ca32f31SSepherosa Ziehau 	/*
1680*5ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
1681*5ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16828892ea20SAggelos Economopoulos 	 */
168389d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
168489d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16858892ea20SAggelos Economopoulos 
1686*5ca32f31SSepherosa Ziehau 	/*
1687*5ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
1688*5ca32f31SSepherosa Ziehau 	 */
168989d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16908892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16918892ea20SAggelos Economopoulos 
1692*5ca32f31SSepherosa Ziehau 	/*
1693*5ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
1694*5ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
1695*5ca32f31SSepherosa Ziehau 	 */
16968892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16978892ea20SAggelos Economopoulos 
16988892ea20SAggelos Economopoulos 	req = tx->req_list;
16998892ea20SAggelos Economopoulos 	seg = tx->seg_list;
17008892ea20SAggelos Economopoulos 	cnt = 0;
17018892ea20SAggelos Economopoulos 	rdma_count = 0;
1702*5ca32f31SSepherosa Ziehau 
1703*5ca32f31SSepherosa Ziehau 	/*
1704*5ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
1705*5ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
1706*5ca32f31SSepherosa Ziehau 	 * this is equal to "count".
17078892ea20SAggelos Economopoulos 	 *
1708*5ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
1709*5ca32f31SSepherosa Ziehau 	 * segment cut.
17108892ea20SAggelos Economopoulos 	 *
1711*5ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
1712*5ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
1713*5ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
1714*5ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
1715*5ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
1716*5ca32f31SSepherosa Ziehau 	 * RDMA.
1717*5ca32f31SSepherosa Ziehau 	 *
1718*5ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
1719*5ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
1720*5ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17218892ea20SAggelos Economopoulos 	 */
17228892ea20SAggelos Economopoulos 
17238892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
1724*5ca32f31SSepherosa Ziehau 		/*
1725*5ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
1726*5ca32f31SSepherosa Ziehau 		 */
17278892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17288892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17298892ea20SAggelos Economopoulos 		len = seg->ds_len;
17308892ea20SAggelos Economopoulos 
17318892ea20SAggelos Economopoulos 		while (len) {
17328892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17338892ea20SAggelos Economopoulos 			seglen = len;
17348892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17358892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17368892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
1737*5ca32f31SSepherosa Ziehau 				/* Payload */
17388892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17398892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17408892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17418892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
1742*5ca32f31SSepherosa Ziehau 				flags_next |=
1743*5ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17448892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17458892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17468892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
1747*5ca32f31SSepherosa Ziehau 				/* Header ends */
17488892ea20SAggelos Economopoulos 				rdma_count = -1;
17498892ea20SAggelos Economopoulos 				cum_len_next = 0;
17508892ea20SAggelos Economopoulos 				seglen = -cum_len;
17518892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17528892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17538892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17548892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17558892ea20SAggelos Economopoulos 			}
17568892ea20SAggelos Economopoulos 
17578892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17588892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17598892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17608892ea20SAggelos Economopoulos 			req->pad = 0;
17618892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17628892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17638892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
1764*5ca32f31SSepherosa Ziehau 			req->flags =
1765*5ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17668892ea20SAggelos Economopoulos 			low += seglen;
17678892ea20SAggelos Economopoulos 			len -= seglen;
17688892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17698892ea20SAggelos Economopoulos 			flags = flags_next;
17708892ea20SAggelos Economopoulos 			req++;
17718892ea20SAggelos Economopoulos 			cnt++;
17728892ea20SAggelos Economopoulos 			rdma_count++;
17738892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17748892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17758892ea20SAggelos Economopoulos 			else
17768892ea20SAggelos Economopoulos 				cksum_offset = 0;
17778892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17788892ea20SAggelos Economopoulos 				goto drop;
17798892ea20SAggelos Economopoulos 		}
17808892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17818892ea20SAggelos Economopoulos 		seg++;
17828892ea20SAggelos Economopoulos 	}
17838892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17848892ea20SAggelos Economopoulos 
17858892ea20SAggelos Economopoulos 	do {
17868892ea20SAggelos Economopoulos 		req--;
17878892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17888892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17898892ea20SAggelos Economopoulos 
17908892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
17918892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17928892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
17938892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
17948892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
17958892ea20SAggelos Economopoulos 		*tx->send_go = 1;
17968892ea20SAggelos Economopoulos 		tx->queue_active = 1;
17978892ea20SAggelos Economopoulos 		tx->activate++;
17988892ea20SAggelos Economopoulos 		wmb();
17998892ea20SAggelos Economopoulos 	}
18008892ea20SAggelos Economopoulos #endif
18018892ea20SAggelos Economopoulos 	return;
18028892ea20SAggelos Economopoulos 
18038892ea20SAggelos Economopoulos drop:
18048892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
18058892ea20SAggelos Economopoulos 	m_freem(m);
1806*5ca32f31SSepherosa Ziehau #if 0
1807*5ca32f31SSepherosa Ziehau 	/* TODO update oerror counter */
1808*5ca32f31SSepherosa Ziehau #endif
18098892ea20SAggelos Economopoulos }
18108892ea20SAggelos Economopoulos 
18118892ea20SAggelos Economopoulos static void
18128892ea20SAggelos Economopoulos mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
18138892ea20SAggelos Economopoulos {
18148892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
18158892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
18168892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
18178892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
181889d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
18198892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
18208892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
18218892ea20SAggelos Economopoulos 
18228892ea20SAggelos Economopoulos 	sc = ss->sc;
18238892ea20SAggelos Economopoulos 	tx = &ss->tx;
18248892ea20SAggelos Economopoulos 
182589d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
182689d55360SSepherosa Ziehau 		if (mxge_pullup_tso(&m))
18278892ea20SAggelos Economopoulos 			return;
18288892ea20SAggelos Economopoulos 	}
182989d55360SSepherosa Ziehau 
1830*5ca32f31SSepherosa Ziehau 	/*
1831*5ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
1832*5ca32f31SSepherosa Ziehau 	 */
183389d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
183489d55360SSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, tx->info[idx].map, &m,
183589d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
183689d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
183789d55360SSepherosa Ziehau 		goto drop;
183889d55360SSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, tx->info[idx].map, BUS_DMASYNC_PREWRITE);
183989d55360SSepherosa Ziehau 	tx->info[idx].m = m;
184089d55360SSepherosa Ziehau 
1841*5ca32f31SSepherosa Ziehau 	/*
1842*5ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
1843*5ca32f31SSepherosa Ziehau 	 */
184489d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1845*5ca32f31SSepherosa Ziehau 		mxge_encap_tso(tx, m, cnt);
184689d55360SSepherosa Ziehau 		return;
184789d55360SSepherosa Ziehau 	}
18488892ea20SAggelos Economopoulos 
18498892ea20SAggelos Economopoulos 	req = tx->req_list;
18508892ea20SAggelos Economopoulos 	cksum_offset = 0;
18518892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18528892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18538892ea20SAggelos Economopoulos 
1854*5ca32f31SSepherosa Ziehau 	/*
1855*5ca32f31SSepherosa Ziehau 	 * Checksum offloading
1856*5ca32f31SSepherosa Ziehau 	 */
185789d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
185889d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18598892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18608892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18618892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18628892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18638892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18648892ea20SAggelos Economopoulos 	} else {
18658892ea20SAggelos Economopoulos 		odd_flag = 0;
18668892ea20SAggelos Economopoulos 	}
18678892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18688892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18698892ea20SAggelos Economopoulos 
1870*5ca32f31SSepherosa Ziehau 	/*
1871*5ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
1872*5ca32f31SSepherosa Ziehau 	 */
18738892ea20SAggelos Economopoulos 	cum_len = 0;
18748892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18758892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18768892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
1877*5ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
1878*5ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18798892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18808892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18818892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18828892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18838892ea20SAggelos Economopoulos 		else
18848892ea20SAggelos Economopoulos 			cksum_offset = 0;
18858892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18868892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18878892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18888892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18898892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18908892ea20SAggelos Economopoulos 		seg++;
18918892ea20SAggelos Economopoulos 		req++;
18928892ea20SAggelos Economopoulos 		req->flags = 0;
18938892ea20SAggelos Economopoulos 	}
18948892ea20SAggelos Economopoulos 	req--;
1895*5ca32f31SSepherosa Ziehau 
1896*5ca32f31SSepherosa Ziehau 	/*
1897*5ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
1898*5ca32f31SSepherosa Ziehau 	 */
18998892ea20SAggelos Economopoulos 	if (cum_len < 60) {
19008892ea20SAggelos Economopoulos 		req++;
19018892ea20SAggelos Economopoulos 		req->addr_low =
19027cc92483SSepherosa Ziehau 		    htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
19038892ea20SAggelos Economopoulos 		req->addr_high =
19047cc92483SSepherosa Ziehau 		    htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
19058892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
19068892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
19078892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
19088892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
19098892ea20SAggelos Economopoulos 		req->rdma_count = 1;
19108892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
19118892ea20SAggelos Economopoulos 		cnt++;
19128892ea20SAggelos Economopoulos 	}
19138892ea20SAggelos Economopoulos 
19148892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
19158892ea20SAggelos Economopoulos #if 0
19168892ea20SAggelos Economopoulos 	/* print what the firmware will see */
19178892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
19186c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
19198892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
19208892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19218892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19228892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19238892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19248892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19258892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19268892ea20SAggelos Economopoulos 	}
19276c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19288892ea20SAggelos Economopoulos #endif
19298892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19308892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19318892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19328892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19338892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19348892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19358892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19368892ea20SAggelos Economopoulos 		tx->activate++;
19378892ea20SAggelos Economopoulos 		wmb();
19388892ea20SAggelos Economopoulos 	}
19398892ea20SAggelos Economopoulos #endif
19408892ea20SAggelos Economopoulos 	return;
19418892ea20SAggelos Economopoulos 
19428892ea20SAggelos Economopoulos drop:
19438892ea20SAggelos Economopoulos 	m_freem(m);
19448892ea20SAggelos Economopoulos 	ss->oerrors++;
19458892ea20SAggelos Economopoulos }
19468892ea20SAggelos Economopoulos 
1947ddbf91b7SSepherosa Ziehau static __inline void
19488892ea20SAggelos Economopoulos mxge_start_locked(struct mxge_slice_state *ss)
19498892ea20SAggelos Economopoulos {
19508892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
19518892ea20SAggelos Economopoulos 	struct mbuf *m;
19528892ea20SAggelos Economopoulos 	struct ifnet *ifp;
19538892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
19548892ea20SAggelos Economopoulos 
19558892ea20SAggelos Economopoulos 	sc = ss->sc;
19568892ea20SAggelos Economopoulos 	ifp = sc->ifp;
19578892ea20SAggelos Economopoulos 	tx = &ss->tx;
19588892ea20SAggelos Economopoulos 	while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) {
1959ac9843a1SSepherosa Ziehau 		m = ifq_dequeue(&ifp->if_snd);
19608892ea20SAggelos Economopoulos 		if (m == NULL) {
19618892ea20SAggelos Economopoulos 			return;
19628892ea20SAggelos Economopoulos 		}
19638892ea20SAggelos Economopoulos 		/* let BPF see it */
19648892ea20SAggelos Economopoulos 		BPF_MTAP(ifp, m);
19658892ea20SAggelos Economopoulos 
19668892ea20SAggelos Economopoulos 		/* give it to the nic */
19678892ea20SAggelos Economopoulos 		mxge_encap(ss, m);
19688892ea20SAggelos Economopoulos 	}
196989d55360SSepherosa Ziehau 
19708892ea20SAggelos Economopoulos 	/* ran out of transmit slots */
19719ed293e0SSepherosa Ziehau 	ifq_set_oactive(&ifp->if_snd);
19728892ea20SAggelos Economopoulos }
19739ed293e0SSepherosa Ziehau 
19748892ea20SAggelos Economopoulos static void
1975f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19768892ea20SAggelos Economopoulos {
19778892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
19788892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
19798892ea20SAggelos Economopoulos 
1980f0a26983SSepherosa Ziehau 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
1981cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
19828892ea20SAggelos Economopoulos 	/* only use the first slice for now */
19838892ea20SAggelos Economopoulos 	ss = &sc->ss[0];
19848892ea20SAggelos Economopoulos 	mxge_start_locked(ss);
19858892ea20SAggelos Economopoulos }
19868892ea20SAggelos Economopoulos 
19878892ea20SAggelos Economopoulos /*
19888892ea20SAggelos Economopoulos  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
19898892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
19908892ea20SAggelos Economopoulos  * pio handler in the nic.   We re-write the first segment's low
19918892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
19928892ea20SAggelos Economopoulos  * in a burst
19938892ea20SAggelos Economopoulos  */
1994ddbf91b7SSepherosa Ziehau static __inline void
19958892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
19968892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
19978892ea20SAggelos Economopoulos {
19988892ea20SAggelos Economopoulos 	uint32_t low;
19998892ea20SAggelos Economopoulos 
20008892ea20SAggelos Economopoulos 	low = src->addr_low;
20018892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
20028892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
20038892ea20SAggelos Economopoulos 	wmb();
20048892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
20058892ea20SAggelos Economopoulos 	wmb();
20068892ea20SAggelos Economopoulos 	src->addr_low = low;
20078892ea20SAggelos Economopoulos 	dst->addr_low = low;
20088892ea20SAggelos Economopoulos 	wmb();
20098892ea20SAggelos Economopoulos }
20108892ea20SAggelos Economopoulos 
20118892ea20SAggelos Economopoulos static int
20128ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20138ebf015eSSepherosa Ziehau     boolean_t init)
20148892ea20SAggelos Economopoulos {
20158892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20168892ea20SAggelos Economopoulos 	struct mbuf *m;
2017363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20188892ea20SAggelos Economopoulos 
20198ebf015eSSepherosa Ziehau 	mflag = MB_DONTWAIT;
20208ebf015eSSepherosa Ziehau 	if (__predict_false(init))
20218ebf015eSSepherosa Ziehau 		mflag = MB_WAIT;
20228ebf015eSSepherosa Ziehau 
20238ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20248892ea20SAggelos Economopoulos 	if (m == NULL) {
20258892ea20SAggelos Economopoulos 		rx->alloc_fail++;
20268892ea20SAggelos Economopoulos 		err = ENOBUFS;
20278ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20288ebf015eSSepherosa Ziehau 			/*
20298ebf015eSSepherosa Ziehau 			 * During initialization, there
20308ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20318ebf015eSSepherosa Ziehau 			 */
20328ebf015eSSepherosa Ziehau 			return err;
20338ebf015eSSepherosa Ziehau 		}
20348892ea20SAggelos Economopoulos 		goto done;
20358892ea20SAggelos Economopoulos 	}
20362823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20378ebf015eSSepherosa Ziehau 
20387d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20397d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20408892ea20SAggelos Economopoulos 	if (err != 0) {
20418ebf015eSSepherosa Ziehau 		m_freem(m);
20428ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20438ebf015eSSepherosa Ziehau 			/*
20448ebf015eSSepherosa Ziehau 			 * During initialization, there
20458ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20468ebf015eSSepherosa Ziehau 			 */
20478ebf015eSSepherosa Ziehau 			return err;
20488ebf015eSSepherosa Ziehau 		}
20498892ea20SAggelos Economopoulos 		goto done;
20508892ea20SAggelos Economopoulos 	}
20518ebf015eSSepherosa Ziehau 
20528892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20538ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20548ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20558892ea20SAggelos Economopoulos 
20568892ea20SAggelos Economopoulos done:
20578892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20588892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20598892ea20SAggelos Economopoulos 	return err;
20608892ea20SAggelos Economopoulos }
20618892ea20SAggelos Economopoulos 
20628892ea20SAggelos Economopoulos static int
2063363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2064363b44f8SSepherosa Ziehau     boolean_t init)
20658892ea20SAggelos Economopoulos {
2066b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20678892ea20SAggelos Economopoulos 	struct mbuf *m;
2068363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2069363b44f8SSepherosa Ziehau 
2070363b44f8SSepherosa Ziehau 	mflag = MB_DONTWAIT;
2071363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2072363b44f8SSepherosa Ziehau 		mflag = MB_WAIT;
20738892ea20SAggelos Economopoulos 
20748892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2075363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2076b9a8961fSSepherosa Ziehau 	else
2077363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
20788892ea20SAggelos Economopoulos 	if (m == NULL) {
20798892ea20SAggelos Economopoulos 		rx->alloc_fail++;
20808892ea20SAggelos Economopoulos 		err = ENOBUFS;
2081363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2082363b44f8SSepherosa Ziehau 			/*
2083363b44f8SSepherosa Ziehau 			 * During initialization, there
2084363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2085363b44f8SSepherosa Ziehau 			 */
2086363b44f8SSepherosa Ziehau 			return err;
2087363b44f8SSepherosa Ziehau 		}
20888892ea20SAggelos Economopoulos 		goto done;
20898892ea20SAggelos Economopoulos 	}
20902823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = rx->mlen;
2091b9a8961fSSepherosa Ziehau 
20927d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2093b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20948892ea20SAggelos Economopoulos 	if (err != 0) {
2095363b44f8SSepherosa Ziehau 		m_freem(m);
2096363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2097363b44f8SSepherosa Ziehau 			/*
2098363b44f8SSepherosa Ziehau 			 * During initialization, there
2099363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2100363b44f8SSepherosa Ziehau 			 */
2101363b44f8SSepherosa Ziehau 			return err;
2102363b44f8SSepherosa Ziehau 		}
21038892ea20SAggelos Economopoulos 		goto done;
21048892ea20SAggelos Economopoulos 	}
2105b9a8961fSSepherosa Ziehau 
21068892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2107363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2108363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21098892ea20SAggelos Economopoulos 
21108892ea20SAggelos Economopoulos done:
2111b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2112b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21138892ea20SAggelos Economopoulos 	return err;
21148892ea20SAggelos Economopoulos }
21158892ea20SAggelos Economopoulos 
21168892ea20SAggelos Economopoulos /*
21178892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21188892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21198892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21208892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21218892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21228892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21238892ea20SAggelos Economopoulos  */
212452cf8dfcSSepherosa Ziehau static __inline uint16_t
21258892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21268892ea20SAggelos Economopoulos {
212752cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
212852cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21298892ea20SAggelos Economopoulos 	uint16_t c;
21308892ea20SAggelos Economopoulos 
213152cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21328892ea20SAggelos Economopoulos 
213352cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21348892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21358892ea20SAggelos Economopoulos 		return 1;
213652cf8dfcSSepherosa Ziehau 
213752cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
213852cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21398892ea20SAggelos Economopoulos 		return 1;
214052cf8dfcSSepherosa Ziehau 
21418892ea20SAggelos Economopoulos #ifdef INET
21428892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21438892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21448892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21458892ea20SAggelos Economopoulos #else
21468892ea20SAggelos Economopoulos 	c = 1;
21478892ea20SAggelos Economopoulos #endif
21488892ea20SAggelos Economopoulos 	c ^= 0xffff;
214952cf8dfcSSepherosa Ziehau 	return c;
21508892ea20SAggelos Economopoulos }
21518892ea20SAggelos Economopoulos 
21528892ea20SAggelos Economopoulos static void
21538892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21548892ea20SAggelos Economopoulos {
21558892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21568892ea20SAggelos Economopoulos 	uint32_t partial;
21578892ea20SAggelos Economopoulos 
21588892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21598892ea20SAggelos Economopoulos 
21608892ea20SAggelos Economopoulos 	/*
216152cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
216252cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21638892ea20SAggelos Economopoulos 	 * header.
21648892ea20SAggelos Economopoulos 	 */
21658892ea20SAggelos Economopoulos 
216652cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21678892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21688892ea20SAggelos Economopoulos 
216952cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
217052cf8dfcSSepherosa Ziehau 	*csum += ~partial;
217152cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
217252cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
217352cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
217452cf8dfcSSepherosa Ziehau 
217552cf8dfcSSepherosa Ziehau 	/*
217652cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
217752cf8dfcSSepherosa Ziehau 	 * later consumers expect this
217852cf8dfcSSepherosa Ziehau 	 */
21798892ea20SAggelos Economopoulos 	*csum = htons(*csum);
21808892ea20SAggelos Economopoulos 
21818892ea20SAggelos Economopoulos 	/* save the tag */
2182b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
21838892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
21848892ea20SAggelos Economopoulos 
21858892ea20SAggelos Economopoulos 	/*
21868892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
21878892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
21888892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
21898892ea20SAggelos Economopoulos 	 * type field is already in place.
21908892ea20SAggelos Economopoulos 	 */
2191b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
21928892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2193b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
21948892ea20SAggelos Economopoulos }
21958892ea20SAggelos Economopoulos 
21968892ea20SAggelos Economopoulos 
219752cf8dfcSSepherosa Ziehau static __inline void
2198eda7db08SSepherosa Ziehau mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
21998892ea20SAggelos Economopoulos {
22008892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
22018892ea20SAggelos Economopoulos 	struct ifnet *ifp;
22028892ea20SAggelos Economopoulos 	struct mbuf *m;
220352cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22048892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
22058892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22068892ea20SAggelos Economopoulos 	int idx;
22078892ea20SAggelos Economopoulos 
22088892ea20SAggelos Economopoulos 	sc = ss->sc;
22098892ea20SAggelos Economopoulos 	ifp = sc->ifp;
22108892ea20SAggelos Economopoulos 	rx = &ss->rx_big;
221152cf8dfcSSepherosa Ziehau 
22128892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2213b9a8961fSSepherosa Ziehau 	rx->cnt++;
221452cf8dfcSSepherosa Ziehau 
221552cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22168892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
221752cf8dfcSSepherosa Ziehau 
221852cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2219363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
222052cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2221d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22228892ea20SAggelos Economopoulos 		return;
22238892ea20SAggelos Economopoulos 	}
22248892ea20SAggelos Economopoulos 
222552cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22268892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22278892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22288892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22298892ea20SAggelos Economopoulos 
223052cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22318892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22328892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22338892ea20SAggelos Economopoulos 
223452cf8dfcSSepherosa Ziehau 	/*
223552cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
223652cf8dfcSSepherosa Ziehau 	 * aligned
223752cf8dfcSSepherosa Ziehau 	 */
22388892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22398892ea20SAggelos Economopoulos 
22408892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22418892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
224252cf8dfcSSepherosa Ziehau 
22438892ea20SAggelos Economopoulos 	ss->ipackets++;
224452cf8dfcSSepherosa Ziehau 
224552cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
224652cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22478892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
224852cf8dfcSSepherosa Ziehau 
224952cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
225089d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
225152cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
225289d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22538892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
225489d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
225589d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22568892ea20SAggelos Economopoulos 	}
2257eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
22588892ea20SAggelos Economopoulos }
22598892ea20SAggelos Economopoulos 
226052cf8dfcSSepherosa Ziehau static __inline void
2261eda7db08SSepherosa Ziehau mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
22628892ea20SAggelos Economopoulos {
22638892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
22648892ea20SAggelos Economopoulos 	struct ifnet *ifp;
226552cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22668892ea20SAggelos Economopoulos 	struct mbuf *m;
22678892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
22688892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22698892ea20SAggelos Economopoulos 	int idx;
22708892ea20SAggelos Economopoulos 
22718892ea20SAggelos Economopoulos 	sc = ss->sc;
22728892ea20SAggelos Economopoulos 	ifp = sc->ifp;
22738892ea20SAggelos Economopoulos 	rx = &ss->rx_small;
227452cf8dfcSSepherosa Ziehau 
22758892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22768892ea20SAggelos Economopoulos 	rx->cnt++;
227752cf8dfcSSepherosa Ziehau 
227852cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22798892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
228052cf8dfcSSepherosa Ziehau 
228152cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22828ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
228352cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2284d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22858892ea20SAggelos Economopoulos 		return;
22868892ea20SAggelos Economopoulos 	}
22878892ea20SAggelos Economopoulos 
228852cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22898892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22908892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22918892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22928892ea20SAggelos Economopoulos 
229352cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22948892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22958892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22968892ea20SAggelos Economopoulos 
229752cf8dfcSSepherosa Ziehau 	/*
229852cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
229952cf8dfcSSepherosa Ziehau 	 * aligned
230052cf8dfcSSepherosa Ziehau 	 */
23018892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
23028892ea20SAggelos Economopoulos 
23038892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
23048892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
230552cf8dfcSSepherosa Ziehau 
23068892ea20SAggelos Economopoulos 	ss->ipackets++;
230752cf8dfcSSepherosa Ziehau 
230852cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
230952cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
23108892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
231152cf8dfcSSepherosa Ziehau 
231252cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
231389d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
231452cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
231589d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
23168892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
231789d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
231889d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
23198892ea20SAggelos Economopoulos 	}
2320eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
23218892ea20SAggelos Economopoulos }
23228892ea20SAggelos Economopoulos 
232352cf8dfcSSepherosa Ziehau static __inline void
23248892ea20SAggelos Economopoulos mxge_clean_rx_done(struct mxge_slice_state *ss)
23258892ea20SAggelos Economopoulos {
23268892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
23278892ea20SAggelos Economopoulos 
23288892ea20SAggelos Economopoulos 	while (rx_done->entry[rx_done->idx].length != 0) {
232952cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
233052cf8dfcSSepherosa Ziehau 
23318892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23328892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
233352cf8dfcSSepherosa Ziehau 
23348892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
233552cf8dfcSSepherosa Ziehau 
23368892ea20SAggelos Economopoulos 		if (length <= (MHLEN - MXGEFW_PAD))
2337eda7db08SSepherosa Ziehau 			mxge_rx_done_small(ss, length, checksum);
23388892ea20SAggelos Economopoulos 		else
2339eda7db08SSepherosa Ziehau 			mxge_rx_done_big(ss, length, checksum);
234052cf8dfcSSepherosa Ziehau 
23418892ea20SAggelos Economopoulos 		rx_done->cnt++;
23428892ea20SAggelos Economopoulos 		rx_done->idx = rx_done->cnt & rx_done->mask;
23438892ea20SAggelos Economopoulos 	}
23448892ea20SAggelos Economopoulos }
23458892ea20SAggelos Economopoulos 
2346ddbf91b7SSepherosa Ziehau static __inline void
23478892ea20SAggelos Economopoulos mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
23488892ea20SAggelos Economopoulos {
23498892ea20SAggelos Economopoulos 	struct ifnet *ifp;
23508892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
23518892ea20SAggelos Economopoulos 	struct mbuf *m;
23528892ea20SAggelos Economopoulos 	bus_dmamap_t map;
23538892ea20SAggelos Economopoulos 	int idx;
23548892ea20SAggelos Economopoulos 
23558892ea20SAggelos Economopoulos 	tx = &ss->tx;
23568892ea20SAggelos Economopoulos 	ifp = ss->sc->ifp;
2357cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
23588892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
23598892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23608892ea20SAggelos Economopoulos 		tx->done++;
23618892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
23628892ea20SAggelos Economopoulos 		/* mbuf and DMA map only attached to the first
23638892ea20SAggelos Economopoulos 		   segment per-mbuf */
23648892ea20SAggelos Economopoulos 		if (m != NULL) {
23658892ea20SAggelos Economopoulos 			ss->obytes += m->m_pkthdr.len;
23668892ea20SAggelos Economopoulos 			if (m->m_flags & M_MCAST)
23678892ea20SAggelos Economopoulos 				ss->omcasts++;
23688892ea20SAggelos Economopoulos 			ss->opackets++;
23698892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
23708892ea20SAggelos Economopoulos 			map = tx->info[idx].map;
23718892ea20SAggelos Economopoulos 			bus_dmamap_unload(tx->dmat, map);
23728892ea20SAggelos Economopoulos 			m_freem(m);
23738892ea20SAggelos Economopoulos 		}
23748892ea20SAggelos Economopoulos 		if (tx->info[idx].flag) {
23758892ea20SAggelos Economopoulos 			tx->info[idx].flag = 0;
23768892ea20SAggelos Economopoulos 			tx->pkt_done++;
23778892ea20SAggelos Economopoulos 		}
23788892ea20SAggelos Economopoulos 	}
23798892ea20SAggelos Economopoulos 
23809ed293e0SSepherosa Ziehau 	/* If we have space, clear OACTIVE to tell the stack that
23818892ea20SAggelos Economopoulos            its OK to send packets */
238289d55360SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1)/4)
23839ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
238489d55360SSepherosa Ziehau 
238589d55360SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
238689d55360SSepherosa Ziehau 		if_devstart(ifp);
238789d55360SSepherosa Ziehau 
23888892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
23898892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
23908892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
23918892ea20SAggelos Economopoulos 		 * are no more transmits pending */
23928892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
23938892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
23948892ea20SAggelos Economopoulos 			tx->queue_active = 0;
23958892ea20SAggelos Economopoulos 			tx->deactivate++;
23968892ea20SAggelos Economopoulos 			wmb();
23978892ea20SAggelos Economopoulos 		}
23988892ea20SAggelos Economopoulos 	}
23998892ea20SAggelos Economopoulos #endif
24008892ea20SAggelos Economopoulos }
24018892ea20SAggelos Economopoulos 
240289d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24038892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24048892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24058892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
24068892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
24078892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
24088892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
24098892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
24108892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
24118892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
24128892ea20SAggelos Economopoulos };
241389d55360SSepherosa Ziehau 
241489d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
241589d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
24168892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
24178892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24188892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
241989d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
242089d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24218892ea20SAggelos Economopoulos };
24228892ea20SAggelos Economopoulos 
24238892ea20SAggelos Economopoulos static void
242489d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24258892ea20SAggelos Economopoulos {
24267cc92483SSepherosa Ziehau 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 0, NULL);
242789d55360SSepherosa Ziehau 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
242889d55360SSepherosa Ziehau 	sc->current_media = media_type;
242989d55360SSepherosa Ziehau 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
24308892ea20SAggelos Economopoulos }
24318892ea20SAggelos Economopoulos 
24328892ea20SAggelos Economopoulos static void
243389d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24348892ea20SAggelos Economopoulos {
2435c7431c78SSepherosa Ziehau 	const char *ptr;
243689d55360SSepherosa Ziehau 	int i;
24378892ea20SAggelos Economopoulos 
243889d55360SSepherosa Ziehau 	ifmedia_removeall(&sc->media);
243989d55360SSepherosa Ziehau 	mxge_media_set(sc, IFM_AUTO);
24408892ea20SAggelos Economopoulos 
24418892ea20SAggelos Economopoulos 	/*
24428892ea20SAggelos Economopoulos 	 * parse the product code to deterimine the interface type
24438892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24448892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24458892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24468892ea20SAggelos Economopoulos 	 */
24478892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24488892ea20SAggelos Economopoulos 	if (ptr == NULL) {
24498892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Missing product code\n");
245089d55360SSepherosa Ziehau 		return;
24518892ea20SAggelos Economopoulos 	}
24528892ea20SAggelos Economopoulos 
24538892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
245489d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24558892ea20SAggelos Economopoulos 		if (ptr == NULL) {
24567cc92483SSepherosa Ziehau 			device_printf(sc->dev, "only %d dashes in PC?!?\n", i);
24578892ea20SAggelos Economopoulos 			return;
24588892ea20SAggelos Economopoulos 		}
24598892ea20SAggelos Economopoulos 	}
246089d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24618892ea20SAggelos Economopoulos 		/* -C is CX4 */
246289d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
246389d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
246489d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24658892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
246689d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
24678892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Quad Ribbon Fiber Media\n");
24688892ea20SAggelos Economopoulos 		/* FreeBSD has no media type for Quad ribbon fiber */
246989d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
247089d55360SSepherosa Ziehau 		/* -R is XFP */
247189d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
247289d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
247389d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
247489d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
247589d55360SSepherosa Ziehau 	} else {
247689d55360SSepherosa Ziehau 		device_printf(sc->dev, "Unknown media type: %c\n", *ptr);
247789d55360SSepherosa Ziehau 	}
24788892ea20SAggelos Economopoulos }
24798892ea20SAggelos Economopoulos 
248089d55360SSepherosa Ziehau /*
248189d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
248289d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
248389d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
248489d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
248589d55360SSepherosa Ziehau  * than in the interrupt handler itself.
248689d55360SSepherosa Ziehau  */
248789d55360SSepherosa Ziehau static void
248889d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
248989d55360SSepherosa Ziehau {
249089d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
24917cc92483SSepherosa Ziehau 	const char *cage_type;
249289d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
249389d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
249489d55360SSepherosa Ziehau 	uint32_t byte;
249589d55360SSepherosa Ziehau 
249689d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
249789d55360SSepherosa Ziehau 
249889d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
24998892ea20SAggelos Economopoulos 		/* -R is XFP */
25008892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
25017cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_xfp_media_types) /
250289d55360SSepherosa Ziehau 		    sizeof(mxge_xfp_media_types[0]);
25038892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25048892ea20SAggelos Economopoulos 		cage_type = "XFP";
250589d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25068892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25078892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
25087cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_sfp_media_types) /
250989d55360SSepherosa Ziehau 		    sizeof(mxge_sfp_media_types[0]);
25108892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25118892ea20SAggelos Economopoulos 		byte = 3;
251289d55360SSepherosa Ziehau 	} else {
251389d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25148892ea20SAggelos Economopoulos 		return;
25158892ea20SAggelos Economopoulos 	}
25168892ea20SAggelos Economopoulos 
25178892ea20SAggelos Economopoulos 	/*
25188892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25198892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25208892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25218892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25228892ea20SAggelos Economopoulos 	 * a millisecond
25238892ea20SAggelos Economopoulos 	 */
25248892ea20SAggelos Economopoulos 
25258892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25268892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25278892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
25287cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
25298892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to read XFP\n");
25307cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
25318892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Type R/S with no XFP!?!?\n");
25327cc92483SSepherosa Ziehau 	if (err != MXGEFW_CMD_OK)
25338892ea20SAggelos Economopoulos 		return;
25348892ea20SAggelos Economopoulos 
25357cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25368892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25378892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25387cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25398892ea20SAggelos Economopoulos 		DELAY(1000);
25408892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25418892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25428892ea20SAggelos Economopoulos 	}
25438892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
25448892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed to read %s (%d, %dms)\n",
25458892ea20SAggelos Economopoulos 		    cage_type, err, ms);
25468892ea20SAggelos Economopoulos 		return;
25478892ea20SAggelos Economopoulos 	}
25488892ea20SAggelos Economopoulos 
25498892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25507cc92483SSepherosa Ziehau 		if (bootverbose) {
25518892ea20SAggelos Economopoulos 			device_printf(sc->dev, "%s:%s\n", cage_type,
25528892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25537cc92483SSepherosa Ziehau 		}
255489d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
255589d55360SSepherosa Ziehau 			mxge_media_init(sc);
255689d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
255789d55360SSepherosa Ziehau 		}
25588892ea20SAggelos Economopoulos 		return;
25598892ea20SAggelos Economopoulos 	}
25608892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25618892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25627cc92483SSepherosa Ziehau 			if (bootverbose) {
25637cc92483SSepherosa Ziehau 				device_printf(sc->dev, "%s:%s\n", cage_type,
25648892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25657cc92483SSepherosa Ziehau 			}
25668892ea20SAggelos Economopoulos 
256789d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
256889d55360SSepherosa Ziehau 				mxge_media_init(sc);
256989d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
257089d55360SSepherosa Ziehau 			}
25718892ea20SAggelos Economopoulos 			return;
25728892ea20SAggelos Economopoulos 		}
25738892ea20SAggelos Economopoulos 	}
25747cc92483SSepherosa Ziehau 	if (bootverbose) {
25757cc92483SSepherosa Ziehau 		device_printf(sc->dev, "%s media 0x%x unknown\n", cage_type,
25767cc92483SSepherosa Ziehau 		    cmd.data0);
25777cc92483SSepherosa Ziehau 	}
25788892ea20SAggelos Economopoulos }
25798892ea20SAggelos Economopoulos 
25808892ea20SAggelos Economopoulos static void
25818892ea20SAggelos Economopoulos mxge_intr(void *arg)
25828892ea20SAggelos Economopoulos {
25838892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss = arg;
25848892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
25858892ea20SAggelos Economopoulos 	mcp_irq_data_t *stats = ss->fw_stats;
25868892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx = &ss->tx;
25878892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
25888892ea20SAggelos Economopoulos 	uint32_t send_done_count;
25898892ea20SAggelos Economopoulos 	uint8_t valid;
25908892ea20SAggelos Economopoulos 
25918892ea20SAggelos Economopoulos 
25928892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
25938892ea20SAggelos Economopoulos 	/* an interrupt on a non-zero slice is implicitly valid
25948892ea20SAggelos Economopoulos 	   since MSI-X irqs are not shared */
25958892ea20SAggelos Economopoulos 	if (ss != sc->ss) {
25968892ea20SAggelos Economopoulos 		mxge_clean_rx_done(ss);
25978892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
25988892ea20SAggelos Economopoulos 		return;
25998892ea20SAggelos Economopoulos 	}
26008892ea20SAggelos Economopoulos #endif
26018892ea20SAggelos Economopoulos 
26028892ea20SAggelos Economopoulos 	/* make sure the DMA has finished */
26038892ea20SAggelos Economopoulos 	if (!stats->valid) {
26048892ea20SAggelos Economopoulos 		return;
26058892ea20SAggelos Economopoulos 	}
26068892ea20SAggelos Economopoulos 	valid = stats->valid;
26078892ea20SAggelos Economopoulos 
260889d55360SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_LEGACY) {
26098892ea20SAggelos Economopoulos 		/* lower legacy IRQ  */
26108892ea20SAggelos Economopoulos 		*sc->irq_deassert = 0;
26118892ea20SAggelos Economopoulos 		if (!mxge_deassert_wait)
26128892ea20SAggelos Economopoulos 			/* don't wait for conf. that irq is low */
26138892ea20SAggelos Economopoulos 			stats->valid = 0;
26148892ea20SAggelos Economopoulos 	} else {
26158892ea20SAggelos Economopoulos 		stats->valid = 0;
26168892ea20SAggelos Economopoulos 	}
26178892ea20SAggelos Economopoulos 
26188892ea20SAggelos Economopoulos 	/* loop while waiting for legacy irq deassertion */
26198892ea20SAggelos Economopoulos 	do {
26208892ea20SAggelos Economopoulos 		/* check for transmit completes and receives */
26218892ea20SAggelos Economopoulos 		send_done_count = be32toh(stats->send_done_count);
26228892ea20SAggelos Economopoulos 		while ((send_done_count != tx->pkt_done) ||
26238892ea20SAggelos Economopoulos 		       (rx_done->entry[rx_done->idx].length != 0)) {
26248892ea20SAggelos Economopoulos 			if (send_done_count != tx->pkt_done)
26258892ea20SAggelos Economopoulos 				mxge_tx_done(ss, (int)send_done_count);
26268892ea20SAggelos Economopoulos 			mxge_clean_rx_done(ss);
26278892ea20SAggelos Economopoulos 			send_done_count = be32toh(stats->send_done_count);
26288892ea20SAggelos Economopoulos 		}
262989d55360SSepherosa Ziehau 		if (sc->irq_type == PCI_INTR_TYPE_LEGACY && mxge_deassert_wait)
26308892ea20SAggelos Economopoulos 			wmb();
26318892ea20SAggelos Economopoulos 	} while (*((volatile uint8_t *) &stats->valid));
26328892ea20SAggelos Economopoulos 
26338892ea20SAggelos Economopoulos 	/* fw link & error stats meaningful only on the first slice */
26348892ea20SAggelos Economopoulos 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
26358892ea20SAggelos Economopoulos 		if (sc->link_state != stats->link_up) {
26368892ea20SAggelos Economopoulos 			sc->link_state = stats->link_up;
26378892ea20SAggelos Economopoulos 			if (sc->link_state) {
263873a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_UP;
263973a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26407cc92483SSepherosa Ziehau 				if (bootverbose)
26418892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link up\n");
26428892ea20SAggelos Economopoulos 			} else {
264373a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_DOWN;
264473a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26457cc92483SSepherosa Ziehau 				if (bootverbose)
26468892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link down\n");
26478892ea20SAggelos Economopoulos 			}
26488892ea20SAggelos Economopoulos 			sc->need_media_probe = 1;
26498892ea20SAggelos Economopoulos 		}
26508892ea20SAggelos Economopoulos 		if (sc->rdma_tags_available !=
26518892ea20SAggelos Economopoulos 		    be32toh(stats->rdma_tags_available)) {
26528892ea20SAggelos Economopoulos 			sc->rdma_tags_available =
26538892ea20SAggelos Economopoulos 				be32toh(stats->rdma_tags_available);
26548892ea20SAggelos Economopoulos 			device_printf(sc->dev, "RDMA timed out! %d tags "
26558892ea20SAggelos Economopoulos 				      "left\n", sc->rdma_tags_available);
26568892ea20SAggelos Economopoulos 		}
26578892ea20SAggelos Economopoulos 
26588892ea20SAggelos Economopoulos 		if (stats->link_down) {
26598892ea20SAggelos Economopoulos 			sc->down_cnt += stats->link_down;
26608892ea20SAggelos Economopoulos 			sc->link_state = 0;
2661f0115d64SAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
2662f0115d64SAggelos Economopoulos 			if_link_state_change(sc->ifp);
26638892ea20SAggelos Economopoulos 		}
26648892ea20SAggelos Economopoulos 	}
26658892ea20SAggelos Economopoulos 
26668892ea20SAggelos Economopoulos 	/* check to see if we have rx token to pass back */
26678892ea20SAggelos Economopoulos 	if (valid & 0x1)
26688892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
26698892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
26708892ea20SAggelos Economopoulos }
26718892ea20SAggelos Economopoulos 
26728892ea20SAggelos Economopoulos static void
26738892ea20SAggelos Economopoulos mxge_init(void *arg)
26748892ea20SAggelos Economopoulos {
267589d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
267689d55360SSepherosa Ziehau 
267789d55360SSepherosa Ziehau 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
267889d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
267989d55360SSepherosa Ziehau 		mxge_open(sc);
26808892ea20SAggelos Economopoulos }
26818892ea20SAggelos Economopoulos 
26828892ea20SAggelos Economopoulos static void
26838892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
26848892ea20SAggelos Economopoulos {
26858892ea20SAggelos Economopoulos 	int i;
26868892ea20SAggelos Economopoulos 
26878892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
26888892ea20SAggelos Economopoulos 		if (ss->rx_big.info[i].m == NULL)
26898892ea20SAggelos Economopoulos 			continue;
26908892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->rx_big.dmat,
26918892ea20SAggelos Economopoulos 				  ss->rx_big.info[i].map);
26928892ea20SAggelos Economopoulos 		m_freem(ss->rx_big.info[i].m);
26938892ea20SAggelos Economopoulos 		ss->rx_big.info[i].m = NULL;
26948892ea20SAggelos Economopoulos 	}
26958892ea20SAggelos Economopoulos 
26968892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
26978892ea20SAggelos Economopoulos 		if (ss->rx_small.info[i].m == NULL)
26988892ea20SAggelos Economopoulos 			continue;
26998892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->rx_small.dmat,
27008892ea20SAggelos Economopoulos 				  ss->rx_small.info[i].map);
27018892ea20SAggelos Economopoulos 		m_freem(ss->rx_small.info[i].m);
27028892ea20SAggelos Economopoulos 		ss->rx_small.info[i].m = NULL;
27038892ea20SAggelos Economopoulos 	}
27048892ea20SAggelos Economopoulos 
27058892ea20SAggelos Economopoulos 	/* transmit ring used only on the first slice */
27068892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
27078892ea20SAggelos Economopoulos 		return;
27088892ea20SAggelos Economopoulos 
27098892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
27108892ea20SAggelos Economopoulos 		ss->tx.info[i].flag = 0;
27118892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
27128892ea20SAggelos Economopoulos 			continue;
27138892ea20SAggelos Economopoulos 		bus_dmamap_unload(ss->tx.dmat,
27148892ea20SAggelos Economopoulos 				  ss->tx.info[i].map);
27158892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
27168892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
27178892ea20SAggelos Economopoulos 	}
27188892ea20SAggelos Economopoulos }
27198892ea20SAggelos Economopoulos 
27208892ea20SAggelos Economopoulos static void
27218892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
27228892ea20SAggelos Economopoulos {
27238892ea20SAggelos Economopoulos 	int slice;
27248892ea20SAggelos Economopoulos 
27258892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
27268892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
27278892ea20SAggelos Economopoulos }
27288892ea20SAggelos Economopoulos 
27298892ea20SAggelos Economopoulos static void
27308892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
27318892ea20SAggelos Economopoulos {
27328892ea20SAggelos Economopoulos 	int i;
27338892ea20SAggelos Economopoulos 
2734798c3369SSepherosa Ziehau 	if (ss->rx_done.entry != NULL) {
27358892ea20SAggelos Economopoulos 		mxge_dma_free(&ss->rx_done.dma);
27368892ea20SAggelos Economopoulos 		ss->rx_done.entry = NULL;
2737798c3369SSepherosa Ziehau 	}
27388892ea20SAggelos Economopoulos 
2739798c3369SSepherosa Ziehau 	if (ss->tx.req_bytes != NULL) {
2740d777b84fSAggelos Economopoulos 		kfree(ss->tx.req_bytes, M_DEVBUF);
27418892ea20SAggelos Economopoulos 		ss->tx.req_bytes = NULL;
2742798c3369SSepherosa Ziehau 	}
27438892ea20SAggelos Economopoulos 
2744798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2745d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
27468892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2747798c3369SSepherosa Ziehau 	}
27488892ea20SAggelos Economopoulos 
2749798c3369SSepherosa Ziehau 	if (ss->rx_small.shadow != NULL) {
2750d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.shadow, M_DEVBUF);
27518892ea20SAggelos Economopoulos 		ss->rx_small.shadow = NULL;
2752798c3369SSepherosa Ziehau 	}
27538892ea20SAggelos Economopoulos 
2754798c3369SSepherosa Ziehau 	if (ss->rx_big.shadow != NULL) {
2755d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.shadow, M_DEVBUF);
27568892ea20SAggelos Economopoulos 		ss->rx_big.shadow = NULL;
2757798c3369SSepherosa Ziehau 	}
27588892ea20SAggelos Economopoulos 
27598892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
27608892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
27618892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
27628892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
27638892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
27648892ea20SAggelos Economopoulos 			}
27658892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
27668892ea20SAggelos Economopoulos 		}
2767d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
27688892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2769798c3369SSepherosa Ziehau 	}
27708892ea20SAggelos Economopoulos 
27718892ea20SAggelos Economopoulos 	if (ss->rx_small.info != NULL) {
27728892ea20SAggelos Economopoulos 		if (ss->rx_small.dmat != NULL) {
27738892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_small.mask; i++) {
27748892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_small.dmat,
27758892ea20SAggelos Economopoulos 				    ss->rx_small.info[i].map);
27768892ea20SAggelos Economopoulos 			}
27778892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_small.dmat,
27788892ea20SAggelos Economopoulos 			    ss->rx_small.extra_map);
27798892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_small.dmat);
27808892ea20SAggelos Economopoulos 		}
2781d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.info, M_DEVBUF);
27828892ea20SAggelos Economopoulos 		ss->rx_small.info = NULL;
2783798c3369SSepherosa Ziehau 	}
27848892ea20SAggelos Economopoulos 
27858892ea20SAggelos Economopoulos 	if (ss->rx_big.info != NULL) {
27868892ea20SAggelos Economopoulos 		if (ss->rx_big.dmat != NULL) {
27878892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_big.mask; i++) {
27888892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_big.dmat,
27898892ea20SAggelos Economopoulos 				    ss->rx_big.info[i].map);
27908892ea20SAggelos Economopoulos 			}
27918892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_big.dmat,
27928892ea20SAggelos Economopoulos 			    ss->rx_big.extra_map);
27938892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_big.dmat);
27948892ea20SAggelos Economopoulos 		}
2795d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.info, M_DEVBUF);
27968892ea20SAggelos Economopoulos 		ss->rx_big.info = NULL;
27978892ea20SAggelos Economopoulos 	}
2798798c3369SSepherosa Ziehau }
27998892ea20SAggelos Economopoulos 
28008892ea20SAggelos Economopoulos static void
28018892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
28028892ea20SAggelos Economopoulos {
28038892ea20SAggelos Economopoulos 	int slice;
28048892ea20SAggelos Economopoulos 
2805798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2806798c3369SSepherosa Ziehau 		return;
2807798c3369SSepherosa Ziehau 
28088892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
28098892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
28108892ea20SAggelos Economopoulos }
28118892ea20SAggelos Economopoulos 
28128892ea20SAggelos Economopoulos static int
28138892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28148892ea20SAggelos Economopoulos     int tx_ring_entries)
28158892ea20SAggelos Economopoulos {
28168892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
28178892ea20SAggelos Economopoulos 	size_t bytes;
28188892ea20SAggelos Economopoulos 	int err, i;
28198892ea20SAggelos Economopoulos 
28207cc92483SSepherosa Ziehau 	/*
28217cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
28227cc92483SSepherosa Ziehau 	 */
28238892ea20SAggelos Economopoulos 
28248892ea20SAggelos Economopoulos 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
28258892ea20SAggelos Economopoulos 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
28268892ea20SAggelos Economopoulos 
28277cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
28288892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow);
2829d777b84fSAggelos Economopoulos 	ss->rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28308892ea20SAggelos Economopoulos 
28318892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow);
2832d777b84fSAggelos Economopoulos 	ss->rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28338892ea20SAggelos Economopoulos 
28347cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
28358892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.info);
2836d777b84fSAggelos Economopoulos 	ss->rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28378892ea20SAggelos Economopoulos 
28388892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.info);
2839d777b84fSAggelos Economopoulos 	ss->rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28408892ea20SAggelos Economopoulos 
28417cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
28428892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28438892ea20SAggelos Economopoulos 				 1,			/* alignment */
28448892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28458892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28468892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28478892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
28488892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
28498892ea20SAggelos Economopoulos 				 1,			/* num segs */
28508892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
28517cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
28527cc92483SSepherosa Ziehau 				 			/* flags */
28538892ea20SAggelos Economopoulos 				 &ss->rx_small.dmat);	/* tag */
28548892ea20SAggelos Economopoulos 	if (err != 0) {
28558892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
28568892ea20SAggelos Economopoulos 		    err);
28573598cc14SSascha Wildner 		return err;
28588892ea20SAggelos Economopoulos 	}
28598892ea20SAggelos Economopoulos 
2860798c3369SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2861798c3369SSepherosa Ziehau 	    &ss->rx_small.extra_map);
2862798c3369SSepherosa Ziehau 	if (err != 0) {
2863798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
2864798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_small.dmat);
2865798c3369SSepherosa Ziehau 		ss->rx_small.dmat = NULL;
2866798c3369SSepherosa Ziehau 		return err;
2867798c3369SSepherosa Ziehau 	}
2868798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_small.mask; i++) {
2869798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2870798c3369SSepherosa Ziehau 		    &ss->rx_small.info[i].map);
2871798c3369SSepherosa Ziehau 		if (err != 0) {
2872798c3369SSepherosa Ziehau 			int j;
2873798c3369SSepherosa Ziehau 
2874798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
2875798c3369SSepherosa Ziehau 
2876798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2877798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_small.dmat,
2878798c3369SSepherosa Ziehau 				    ss->rx_small.info[j].map);
2879798c3369SSepherosa Ziehau 			}
2880798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_small.dmat,
2881798c3369SSepherosa Ziehau 			    ss->rx_small.extra_map);
2882798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_small.dmat);
2883798c3369SSepherosa Ziehau 			ss->rx_small.dmat = NULL;
2884798c3369SSepherosa Ziehau 			return err;
2885798c3369SSepherosa Ziehau 		}
2886798c3369SSepherosa Ziehau 	}
2887798c3369SSepherosa Ziehau 
28888892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28898892ea20SAggelos Economopoulos 				 1,			/* alignment */
28908892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28918892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28928892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28938892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2894b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
2895b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
28968892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
28977cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
28987cc92483SSepherosa Ziehau 				 			/* flags */
28998892ea20SAggelos Economopoulos 				 &ss->rx_big.dmat);	/* tag */
29008892ea20SAggelos Economopoulos 	if (err != 0) {
29018892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
29028892ea20SAggelos Economopoulos 		    err);
29033598cc14SSascha Wildner 		return err;
29048892ea20SAggelos Economopoulos 	}
29057cc92483SSepherosa Ziehau 
29067cc92483SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
29078892ea20SAggelos Economopoulos 	    &ss->rx_big.extra_map);
29088892ea20SAggelos Economopoulos 	if (err != 0) {
29097cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
2910798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_big.dmat);
2911798c3369SSepherosa Ziehau 		ss->rx_big.dmat = NULL;
29123598cc14SSascha Wildner 		return err;
29138892ea20SAggelos Economopoulos 	}
2914798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
2915798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
2916798c3369SSepherosa Ziehau 		    &ss->rx_big.info[i].map);
2917798c3369SSepherosa Ziehau 		if (err != 0) {
2918798c3369SSepherosa Ziehau 			int j;
2919798c3369SSepherosa Ziehau 
2920798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
2921798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2922798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_big.dmat,
2923798c3369SSepherosa Ziehau 				    ss->rx_big.info[j].map);
2924798c3369SSepherosa Ziehau 			}
2925798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_big.dmat,
2926798c3369SSepherosa Ziehau 			    ss->rx_big.extra_map);
2927798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_big.dmat);
2928798c3369SSepherosa Ziehau 			ss->rx_big.dmat = NULL;
2929798c3369SSepherosa Ziehau 			return err;
2930798c3369SSepherosa Ziehau 		}
2931798c3369SSepherosa Ziehau 	}
29328892ea20SAggelos Economopoulos 
29337cc92483SSepherosa Ziehau 	/*
29347cc92483SSepherosa Ziehau 	 * Now allocate TX resources
29357cc92483SSepherosa Ziehau 	 */
29368892ea20SAggelos Economopoulos 
29378892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
29388892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
29398892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
29408892ea20SAggelos Economopoulos 		return 0;
29418892ea20SAggelos Economopoulos #endif
29428892ea20SAggelos Economopoulos 
29438892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
29448892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
29458892ea20SAggelos Economopoulos 
29467cc92483SSepherosa Ziehau 	/* Allocate the tx request copy block XXX */
29477cc92483SSepherosa Ziehau 	bytes = 8 + sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
2948d777b84fSAggelos Economopoulos 	ss->tx.req_bytes = kmalloc(bytes, M_DEVBUF, M_WAITOK);
29497cc92483SSepherosa Ziehau 	/* Ensure req_list entries are aligned to 8 bytes */
29508892ea20SAggelos Economopoulos 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
29518892ea20SAggelos Economopoulos 	    ((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
29528892ea20SAggelos Economopoulos 
29537cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
29548892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
29557cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
29568892ea20SAggelos Economopoulos 
29577cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
29588892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
2959d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29608892ea20SAggelos Economopoulos 
29617cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
29628892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29638892ea20SAggelos Economopoulos 				 1,			/* alignment */
29648892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
29658892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29668892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29678892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
29687cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
29697cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
29707cc92483SSepherosa Ziehau 				 			/* maxsize */
29718892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
29728892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
29737cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
29747cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
29758892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
29768892ea20SAggelos Economopoulos 	if (err != 0) {
29777cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
29783598cc14SSascha Wildner 		return err;
29798892ea20SAggelos Economopoulos 	}
29808892ea20SAggelos Economopoulos 
29817cc92483SSepherosa Ziehau 	/*
29827cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
29837cc92483SSepherosa Ziehau 	 */
29848892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
29857cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
29867cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
29878892ea20SAggelos Economopoulos 		if (err != 0) {
2988798c3369SSepherosa Ziehau 			int j;
2989798c3369SSepherosa Ziehau 
29907cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
2991798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2992798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
2993798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
2994798c3369SSepherosa Ziehau 			}
2995798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
2996798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
29973598cc14SSascha Wildner 			return err;
29988892ea20SAggelos Economopoulos 		}
29998892ea20SAggelos Economopoulos 	}
30008892ea20SAggelos Economopoulos 	return 0;
30018892ea20SAggelos Economopoulos }
30028892ea20SAggelos Economopoulos 
30038892ea20SAggelos Economopoulos static int
30048892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
30058892ea20SAggelos Economopoulos {
30068892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30078892ea20SAggelos Economopoulos 	int tx_ring_size;
30088892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
30098892ea20SAggelos Economopoulos 	int err, slice;
30108892ea20SAggelos Economopoulos 
30117cc92483SSepherosa Ziehau 	/* Get ring sizes */
30128892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30138892ea20SAggelos Economopoulos 	if (err != 0) {
30148892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3015798c3369SSepherosa Ziehau 		return err;
30168892ea20SAggelos Economopoulos 	}
30177cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
30188892ea20SAggelos Economopoulos 
30198892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
30208892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof(mcp_dma_addr_t);
3021f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3022f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
30238892ea20SAggelos Economopoulos 
30248892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
30258892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30267cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3027798c3369SSepherosa Ziehau 		if (err != 0) {
3028798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3029798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3030798c3369SSepherosa Ziehau 			return err;
3031798c3369SSepherosa Ziehau 		}
30328892ea20SAggelos Economopoulos 	}
30338892ea20SAggelos Economopoulos 	return 0;
30348892ea20SAggelos Economopoulos }
30358892ea20SAggelos Economopoulos 
30368892ea20SAggelos Economopoulos static void
3037b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
30388892ea20SAggelos Economopoulos {
3039b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
30408892ea20SAggelos Economopoulos 
30418892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
30428892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3043b9a8961fSSepherosa Ziehau 	} else {
3044b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
30458892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
30468892ea20SAggelos Economopoulos 	}
30478892ea20SAggelos Economopoulos }
30488892ea20SAggelos Economopoulos 
30498892ea20SAggelos Economopoulos static int
3050b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
30518892ea20SAggelos Economopoulos {
30528892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30538892ea20SAggelos Economopoulos 	int err, i, slice;
30548892ea20SAggelos Economopoulos 
3055308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
30568892ea20SAggelos Economopoulos 
3057308781adSSepherosa Ziehau 	/*
3058308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3059308781adSSepherosa Ziehau 	 */
30608892ea20SAggelos Economopoulos 	err = 0;
30618892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30628892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
30638892ea20SAggelos Economopoulos 	if (slice == 0) {
30648892ea20SAggelos Economopoulos #endif
30658892ea20SAggelos Economopoulos 		cmd.data0 = slice;
3066308781adSSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3067308781adSSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3068308781adSSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
30698892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
3070308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
30718892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
3072308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
30738892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30748892ea20SAggelos Economopoulos 	}
30758892ea20SAggelos Economopoulos #endif
3076308781adSSepherosa Ziehau 
30778892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3078308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
30798892ea20SAggelos Economopoulos 	ss->rx_small.lanai =
3080308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3081308781adSSepherosa Ziehau 
30828892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3083308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
30848892ea20SAggelos Economopoulos 	ss->rx_big.lanai =
3085308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
30868892ea20SAggelos Economopoulos 
30878892ea20SAggelos Economopoulos 	if (err != 0) {
3088308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
30898892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
30908892ea20SAggelos Economopoulos 		return EIO;
30918892ea20SAggelos Economopoulos 	}
30928892ea20SAggelos Economopoulos 
3093308781adSSepherosa Ziehau 	/*
3094308781adSSepherosa Ziehau 	 * Stock small receive ring
3095308781adSSepherosa Ziehau 	 */
30968892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
30978ebf015eSSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_small,
30988ebf015eSSepherosa Ziehau 		    ss->rx_small.info[i].map, i, TRUE);
30998892ea20SAggelos Economopoulos 		if (err) {
3100308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
3101308781adSSepherosa Ziehau 			    ss->rx_small.mask + 1);
31028892ea20SAggelos Economopoulos 			return ENOMEM;
31038892ea20SAggelos Economopoulos 		}
31048892ea20SAggelos Economopoulos 	}
3105308781adSSepherosa Ziehau 
3106308781adSSepherosa Ziehau 	/*
3107308781adSSepherosa Ziehau 	 * Stock big receive ring
3108308781adSSepherosa Ziehau 	 */
31098892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
31108892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
31118892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
31128892ea20SAggelos Economopoulos 	}
3113308781adSSepherosa Ziehau 
31148892ea20SAggelos Economopoulos 	ss->rx_big.cl_size = cl_size;
31158892ea20SAggelos Economopoulos 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
3116b915556eSAggelos Economopoulos 	    EVL_ENCAPLEN + MXGEFW_PAD;
3117308781adSSepherosa Ziehau 
3118b9a8961fSSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
3119363b44f8SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_big,
3120363b44f8SSepherosa Ziehau 		    ss->rx_big.info[i].map, i, TRUE);
31218892ea20SAggelos Economopoulos 		if (err) {
3122308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
3123308781adSSepherosa Ziehau 			    ss->rx_big.mask + 1);
31248892ea20SAggelos Economopoulos 			return ENOMEM;
31258892ea20SAggelos Economopoulos 		}
31268892ea20SAggelos Economopoulos 	}
31278892ea20SAggelos Economopoulos 	return 0;
31288892ea20SAggelos Economopoulos }
31298892ea20SAggelos Economopoulos 
31308892ea20SAggelos Economopoulos static int
31318892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
31328892ea20SAggelos Economopoulos {
31336ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
31348892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3135b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
31368892ea20SAggelos Economopoulos 	bus_addr_t bus;
31378892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
31388892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
31398892ea20SAggelos Economopoulos 
31406ee6cba3SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
31416ee6cba3SSepherosa Ziehau 
31428892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
31436ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
31448892ea20SAggelos Economopoulos 
31458892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
31468892ea20SAggelos Economopoulos 	if (err != 0) {
31476ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
31488892ea20SAggelos Economopoulos 		return EIO;
31498892ea20SAggelos Economopoulos 	}
31508892ea20SAggelos Economopoulos 
31518892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
31526ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
31538892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
31546ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
31558892ea20SAggelos Economopoulos 
31566ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
31578892ea20SAggelos Economopoulos 		if (err != 0) {
31586ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
31598892ea20SAggelos Economopoulos 			return err;
31608892ea20SAggelos Economopoulos 		}
31618892ea20SAggelos Economopoulos 
31626ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
31638892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
31648892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
31658892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
31668892ea20SAggelos Economopoulos 
31678892ea20SAggelos Economopoulos 		cmd.data0 = 1;
31686ee6cba3SSepherosa Ziehau 		cmd.data1 = MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
31698892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
31708892ea20SAggelos Economopoulos 		if (err != 0) {
31716ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
31728892ea20SAggelos Economopoulos 			return err;
31738892ea20SAggelos Economopoulos 		}
31748892ea20SAggelos Economopoulos 	}
31758892ea20SAggelos Economopoulos 
317689d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
317789d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
317889d55360SSepherosa Ziehau 	if (err) {
31796ee6cba3SSepherosa Ziehau 		/*
31806ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
31816ee6cba3SSepherosa Ziehau 		 */
31826ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
31836ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
31846ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
31856ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
318689d55360SSepherosa Ziehau 	}
31878892ea20SAggelos Economopoulos 
3188b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
31898892ea20SAggelos Economopoulos 
3190b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
31916ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
31926ee6cba3SSepherosa Ziehau 	/*
31936ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
31946ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
31956ee6cba3SSepherosa Ziehau 	 */
31966ee6cba3SSepherosa Ziehau 
31976ee6cba3SSepherosa Ziehau 	/*
31986ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
31996ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
32006ee6cba3SSepherosa Ziehau 	 * of two. Luckily, FreeBSD's clusters are powers of two
32016ee6cba3SSepherosa Ziehau 	 */
32026ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
32038892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
32046ee6cba3SSepherosa Ziehau 
3205b9a8961fSSepherosa Ziehau 	/* XXX need to cut MXGEFW_PAD here? */
32068892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
32076ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
32086ee6cba3SSepherosa Ziehau 
3209b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
32108892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32118892ea20SAggelos Economopoulos 
32128892ea20SAggelos Economopoulos 	if (err != 0) {
32136ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32148892ea20SAggelos Economopoulos 		goto abort;
32158892ea20SAggelos Economopoulos 	}
32168892ea20SAggelos Economopoulos 
32178892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
32188892ea20SAggelos Economopoulos 	for (slice = 0;
32198892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
32208892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
32218892ea20SAggelos Economopoulos #else
32228892ea20SAggelos Economopoulos 	     slice < 1;
32238892ea20SAggelos Economopoulos #endif
32248892ea20SAggelos Economopoulos 	     slice++) {
32258892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
32267cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32277cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32288892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
32298892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
32308892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32318892ea20SAggelos Economopoulos 	}
32328892ea20SAggelos Economopoulos 
32338892ea20SAggelos Economopoulos 	if (err != 0) {
32347cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
32358892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
32368892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
32378892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
32386ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
32398892ea20SAggelos Economopoulos 		    &cmd);
32406ee6cba3SSepherosa Ziehau 
32418892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
32428892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
32438892ea20SAggelos Economopoulos 	} else {
32448892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
32458892ea20SAggelos Economopoulos 	}
32468892ea20SAggelos Economopoulos 
32478892ea20SAggelos Economopoulos 	if (err != 0) {
32486ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32498892ea20SAggelos Economopoulos 		goto abort;
32508892ea20SAggelos Economopoulos 	}
32518892ea20SAggelos Economopoulos 
32528892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3253b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
32548892ea20SAggelos Economopoulos 		if (err != 0) {
32556ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
32568892ea20SAggelos Economopoulos 			goto abort;
32578892ea20SAggelos Economopoulos 		}
32588892ea20SAggelos Economopoulos 	}
32598892ea20SAggelos Economopoulos 
32608892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
32618892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
32628892ea20SAggelos Economopoulos 	if (err) {
32636ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
32648892ea20SAggelos Economopoulos 		goto abort;
32658892ea20SAggelos Economopoulos 	}
32666ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
32676ee6cba3SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
32688892ea20SAggelos Economopoulos 
32698892ea20SAggelos Economopoulos 	return 0;
32708892ea20SAggelos Economopoulos 
32718892ea20SAggelos Economopoulos abort:
32728892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
32738892ea20SAggelos Economopoulos 	return err;
32748892ea20SAggelos Economopoulos }
32758892ea20SAggelos Economopoulos 
32762c29ffc6SSepherosa Ziehau static void
327789d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
32788892ea20SAggelos Economopoulos {
32792c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
32808892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32818892ea20SAggelos Economopoulos 	int err, old_down_cnt;
32828892ea20SAggelos Economopoulos 
32832c29ffc6SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
328489d55360SSepherosa Ziehau 
32852c29ffc6SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
32862c29ffc6SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
32872c29ffc6SSepherosa Ziehau 
328889d55360SSepherosa Ziehau 	if (!down) {
32898892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
32908892ea20SAggelos Economopoulos 		wmb();
32912c29ffc6SSepherosa Ziehau 
32928892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
32932c29ffc6SSepherosa Ziehau 		if (err)
32942c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
32952c29ffc6SSepherosa Ziehau 
32968892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
32972c29ffc6SSepherosa Ziehau 			/* Wait for down irq */
32982c29ffc6SSepherosa Ziehau 			lwkt_serialize_exit(ifp->if_serializer);
32998892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
33002c29ffc6SSepherosa Ziehau 			lwkt_serialize_enter(ifp->if_serializer);
33018892ea20SAggelos Economopoulos 		}
33022c29ffc6SSepherosa Ziehau 
33038892ea20SAggelos Economopoulos 		wmb();
33042c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
33052c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
330689d55360SSepherosa Ziehau 	}
33078892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33088892ea20SAggelos Economopoulos }
33098892ea20SAggelos Economopoulos 
33108892ea20SAggelos Economopoulos static void
33118892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
33128892ea20SAggelos Economopoulos {
33138892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33148892ea20SAggelos Economopoulos 	int reg;
331589d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
33168892ea20SAggelos Economopoulos 
33177cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
33188892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
33198892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
33208892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
33218892ea20SAggelos Economopoulos 
332289d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
33238892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
33248892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
33258892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
332689d55360SSepherosa Ziehau 			sc->pectl = pectl;
332789d55360SSepherosa Ziehau 		} else {
33287cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
332989d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
333089d55360SSepherosa Ziehau 		}
33318892ea20SAggelos Economopoulos 	}
33328892ea20SAggelos Economopoulos 
33337cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
33348892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
33358892ea20SAggelos Economopoulos }
33368892ea20SAggelos Economopoulos 
33378892ea20SAggelos Economopoulos static uint32_t
33388892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
33398892ea20SAggelos Economopoulos {
33408892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33418892ea20SAggelos Economopoulos 	uint32_t vs;
33428892ea20SAggelos Economopoulos 
33438892ea20SAggelos Economopoulos 	/* find the vendor specific offset */
33448892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
33458892ea20SAggelos Economopoulos 		device_printf(sc->dev,
33468892ea20SAggelos Economopoulos 			      "could not find vendor specific offset\n");
33478892ea20SAggelos Economopoulos 		return (uint32_t)-1;
33488892ea20SAggelos Economopoulos 	}
33498892ea20SAggelos Economopoulos 	/* enable read32 mode */
33508892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
33518892ea20SAggelos Economopoulos 	/* tell NIC which register to read */
33528892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
33538892ea20SAggelos Economopoulos 	return (pci_read_config(dev, vs + 0x14, 4));
33548892ea20SAggelos Economopoulos }
33558892ea20SAggelos Economopoulos 
335689d55360SSepherosa Ziehau static void
335789d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
33588892ea20SAggelos Economopoulos {
33598892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
336089d55360SSepherosa Ziehau 	int err, running;
33618892ea20SAggelos Economopoulos 	uint32_t reboot;
33628892ea20SAggelos Economopoulos 	uint16_t cmd;
33638892ea20SAggelos Economopoulos 
33648892ea20SAggelos Economopoulos 	err = ENXIO;
33658892ea20SAggelos Economopoulos 
33668892ea20SAggelos Economopoulos 	device_printf(sc->dev, "Watchdog reset!\n");
33678892ea20SAggelos Economopoulos 
33688892ea20SAggelos Economopoulos 	/*
33698892ea20SAggelos Economopoulos 	 * check to see if the NIC rebooted.  If it did, then all of
33708892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
33718892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
33728892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
33738892ea20SAggelos Economopoulos 	 * again
33748892ea20SAggelos Economopoulos 	 */
33758892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33768892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
33778892ea20SAggelos Economopoulos 		/*
33788892ea20SAggelos Economopoulos 		 * maybe the watchdog caught the NIC rebooting; wait
33798892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
33808892ea20SAggelos Economopoulos 		 * back, then give up
33818892ea20SAggelos Economopoulos 		 */
33828892ea20SAggelos Economopoulos 		DELAY(1000*100);
33838892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33848892ea20SAggelos Economopoulos 		if (cmd == 0xffff) {
33858892ea20SAggelos Economopoulos 			device_printf(sc->dev, "NIC disappeared!\n");
33868892ea20SAggelos Economopoulos 		}
33878892ea20SAggelos Economopoulos 	}
33888892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
33898892ea20SAggelos Economopoulos 		/* print the reboot status */
33908892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
33918892ea20SAggelos Economopoulos 		device_printf(sc->dev, "NIC rebooted, status = 0x%x\n",
33928892ea20SAggelos Economopoulos 			      reboot);
339389d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
339489d55360SSepherosa Ziehau 		if (running) {
339589d55360SSepherosa Ziehau 
339689d55360SSepherosa Ziehau 			/*
339789d55360SSepherosa Ziehau 			 * quiesce NIC so that TX routines will not try to
339889d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
339989d55360SSepherosa Ziehau 			 */
340089d55360SSepherosa Ziehau 
340189d55360SSepherosa Ziehau 			/* Mark the link as down */
340289d55360SSepherosa Ziehau 			if (sc->link_state) {
340389d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
340489d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
340589d55360SSepherosa Ziehau 			}
340689d55360SSepherosa Ziehau 			mxge_close(sc, 1);
340789d55360SSepherosa Ziehau 		}
34088892ea20SAggelos Economopoulos 		/* restore PCI configuration space */
34098892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
34108892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
34118892ea20SAggelos Economopoulos 
34128892ea20SAggelos Economopoulos 		/* and redo any changes we made to our config space */
34138892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
34148892ea20SAggelos Economopoulos 
341589d55360SSepherosa Ziehau 		/* reload f/w */
341689d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
341789d55360SSepherosa Ziehau 		if (err) {
34188892ea20SAggelos Economopoulos 			device_printf(sc->dev,
341989d55360SSepherosa Ziehau 				      "Unable to re-load f/w\n");
342089d55360SSepherosa Ziehau 		}
342189d55360SSepherosa Ziehau 		if (running) {
342289d55360SSepherosa Ziehau 			if (!err) {
342389d55360SSepherosa Ziehau 				err = mxge_open(sc);
342489d55360SSepherosa Ziehau 				if_devstart_sched(sc->ifp);
342589d55360SSepherosa Ziehau 			}
342689d55360SSepherosa Ziehau 		}
342789d55360SSepherosa Ziehau 		sc->watchdog_resets++;
342889d55360SSepherosa Ziehau 	} else {
342989d55360SSepherosa Ziehau 		device_printf(sc->dev,
343089d55360SSepherosa Ziehau 			      "NIC did not reboot, not resetting\n");
343189d55360SSepherosa Ziehau 		err = 0;
343289d55360SSepherosa Ziehau 	}
343389d55360SSepherosa Ziehau 	if (err) {
343489d55360SSepherosa Ziehau 		device_printf(sc->dev, "watchdog reset failed\n");
343589d55360SSepherosa Ziehau 	} else {
343689d55360SSepherosa Ziehau 		if (sc->dying == 2)
343789d55360SSepherosa Ziehau 			sc->dying = 0;
343889d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
343989d55360SSepherosa Ziehau 	}
344089d55360SSepherosa Ziehau }
344189d55360SSepherosa Ziehau 
344289d55360SSepherosa Ziehau static void
344389d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
344489d55360SSepherosa Ziehau {
344589d55360SSepherosa Ziehau 	tx = &sc->ss[slice].tx;
344689d55360SSepherosa Ziehau 	device_printf(sc->dev, "slice %d struck? ring state:\n", slice);
34478892ea20SAggelos Economopoulos 	device_printf(sc->dev,
34488892ea20SAggelos Economopoulos 		      "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
34498892ea20SAggelos Economopoulos 		      tx->req, tx->done, tx->queue_active);
34508892ea20SAggelos Economopoulos 	device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n",
34518892ea20SAggelos Economopoulos 			      tx->activate, tx->deactivate);
34528892ea20SAggelos Economopoulos 	device_printf(sc->dev, "pkt_done=%d fw=%d\n",
34538892ea20SAggelos Economopoulos 		      tx->pkt_done,
34548892ea20SAggelos Economopoulos 		      be32toh(sc->ss->fw_stats->send_done_count));
34558892ea20SAggelos Economopoulos }
34568892ea20SAggelos Economopoulos 
34578892ea20SAggelos Economopoulos static int
34588892ea20SAggelos Economopoulos mxge_watchdog(mxge_softc_t *sc)
34598892ea20SAggelos Economopoulos {
34608892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
34618892ea20SAggelos Economopoulos 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
34628892ea20SAggelos Economopoulos 	int i, err = 0;
34638892ea20SAggelos Economopoulos 
34648892ea20SAggelos Economopoulos 	/* see if we have outstanding transmits, which
34658892ea20SAggelos Economopoulos 	   have been pending for more than mxge_ticks */
34668892ea20SAggelos Economopoulos 	for (i = 0;
34678892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
34688892ea20SAggelos Economopoulos 	     (i < sc->num_slices) && (err == 0);
34698892ea20SAggelos Economopoulos #else
34708892ea20SAggelos Economopoulos 	     (i < 1) && (err == 0);
34718892ea20SAggelos Economopoulos #endif
34728892ea20SAggelos Economopoulos 	     i++) {
34738892ea20SAggelos Economopoulos 		tx = &sc->ss[i].tx;
34748892ea20SAggelos Economopoulos 		if (tx->req != tx->done &&
34758892ea20SAggelos Economopoulos 		    tx->watchdog_req != tx->watchdog_done &&
34768892ea20SAggelos Economopoulos 		    tx->done == tx->watchdog_done) {
34778892ea20SAggelos Economopoulos 			/* check for pause blocking before resetting */
347889d55360SSepherosa Ziehau 			if (tx->watchdog_rx_pause == rx_pause) {
347989d55360SSepherosa Ziehau 				mxge_warn_stuck(sc, tx, i);
348089d55360SSepherosa Ziehau 				mxge_watchdog_reset(sc);
348189d55360SSepherosa Ziehau 				return (ENXIO);
348289d55360SSepherosa Ziehau 			}
34838892ea20SAggelos Economopoulos 			else
34848892ea20SAggelos Economopoulos 				device_printf(sc->dev, "Flow control blocking "
34858892ea20SAggelos Economopoulos 					      "xmits, check link partner\n");
34868892ea20SAggelos Economopoulos 		}
34878892ea20SAggelos Economopoulos 
34888892ea20SAggelos Economopoulos 		tx->watchdog_req = tx->req;
34898892ea20SAggelos Economopoulos 		tx->watchdog_done = tx->done;
34908892ea20SAggelos Economopoulos 		tx->watchdog_rx_pause = rx_pause;
34918892ea20SAggelos Economopoulos 	}
34928892ea20SAggelos Economopoulos 
34938892ea20SAggelos Economopoulos 	if (sc->need_media_probe)
34948892ea20SAggelos Economopoulos 		mxge_media_probe(sc);
34958892ea20SAggelos Economopoulos 	return (err);
34968892ea20SAggelos Economopoulos }
34978892ea20SAggelos Economopoulos 
349889d55360SSepherosa Ziehau static u_long
34998892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
35008892ea20SAggelos Economopoulos {
35018892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
350289d55360SSepherosa Ziehau 	u_long pkts = 0;
350389d55360SSepherosa Ziehau 	u_long ipackets = 0, old_ipackets;
350489d55360SSepherosa Ziehau 	u_long opackets = 0, old_opackets;
35058892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
35068892ea20SAggelos Economopoulos 	u_long obytes = 0;
35078892ea20SAggelos Economopoulos 	u_long omcasts = 0;
35088892ea20SAggelos Economopoulos 	u_long odrops = 0;
35098892ea20SAggelos Economopoulos #endif
35108892ea20SAggelos Economopoulos 	u_long oerrors = 0;
35118892ea20SAggelos Economopoulos 	int slice;
35128892ea20SAggelos Economopoulos 
35138892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
35148892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
35158892ea20SAggelos Economopoulos 		ipackets += ss->ipackets;
35168892ea20SAggelos Economopoulos 		opackets += ss->opackets;
35178892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
35188892ea20SAggelos Economopoulos 		obytes += ss->obytes;
35198892ea20SAggelos Economopoulos 		omcasts += ss->omcasts;
35208892ea20SAggelos Economopoulos 		odrops += ss->tx.br->br_drops;
35218892ea20SAggelos Economopoulos #endif
35228892ea20SAggelos Economopoulos 		oerrors += ss->oerrors;
35238892ea20SAggelos Economopoulos 	}
352489d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, old_ipackets);
352589d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, old_opackets);
352689d55360SSepherosa Ziehau 
352789d55360SSepherosa Ziehau 	pkts = ipackets - old_ipackets;
352889d55360SSepherosa Ziehau 	pkts += opackets - old_opackets;
352989d55360SSepherosa Ziehau 
3530d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, ipackets, ipackets);
3531d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, opackets, opackets);
35328892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
35338892ea20SAggelos Economopoulos 	sc->ifp->if_obytes = obytes;
35348892ea20SAggelos Economopoulos 	sc->ifp->if_omcasts = omcasts;
35358892ea20SAggelos Economopoulos 	sc->ifp->if_snd.ifq_drops = odrops;
35368892ea20SAggelos Economopoulos #endif
3537d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, oerrors, oerrors);
353889d55360SSepherosa Ziehau 	return pkts;
35398892ea20SAggelos Economopoulos }
35408892ea20SAggelos Economopoulos 
35418892ea20SAggelos Economopoulos static void
35428892ea20SAggelos Economopoulos mxge_tick(void *arg)
35438892ea20SAggelos Economopoulos {
35448892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
354589d55360SSepherosa Ziehau 	u_long pkts = 0;
35468892ea20SAggelos Economopoulos 	int err = 0;
354789d55360SSepherosa Ziehau 	int running, ticks;
354889d55360SSepherosa Ziehau 	uint16_t cmd;
35498892ea20SAggelos Economopoulos 
35502e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
355189d55360SSepherosa Ziehau 
355289d55360SSepherosa Ziehau 	ticks = mxge_ticks;
355389d55360SSepherosa Ziehau 	running = sc->ifp->if_flags & IFF_RUNNING;
355489d55360SSepherosa Ziehau 	if (running) {
35558892ea20SAggelos Economopoulos 		/* aggregate stats from different slices */
355689d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
35578892ea20SAggelos Economopoulos 		if (!sc->watchdog_countdown) {
35588892ea20SAggelos Economopoulos 			err = mxge_watchdog(sc);
35598892ea20SAggelos Economopoulos 			sc->watchdog_countdown = 4;
35608892ea20SAggelos Economopoulos 		}
35618892ea20SAggelos Economopoulos 		sc->watchdog_countdown--;
356289d55360SSepherosa Ziehau 	}
356389d55360SSepherosa Ziehau 	if (pkts == 0) {
356489d55360SSepherosa Ziehau 		/* ensure NIC did not suffer h/w fault while idle */
356589d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
356689d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
356789d55360SSepherosa Ziehau 			sc->dying = 2;
356889d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
356989d55360SSepherosa Ziehau 			err = ENXIO;
357089d55360SSepherosa Ziehau 		}
357189d55360SSepherosa Ziehau 		/* look less often if NIC is idle */
357289d55360SSepherosa Ziehau 		ticks *= 4;
357389d55360SSepherosa Ziehau 	}
357489d55360SSepherosa Ziehau 
35758892ea20SAggelos Economopoulos 	if (err == 0)
357689d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
357789d55360SSepherosa Ziehau 
35782e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
35798892ea20SAggelos Economopoulos }
35808892ea20SAggelos Economopoulos 
35818892ea20SAggelos Economopoulos static int
35828892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
35838892ea20SAggelos Economopoulos {
35848892ea20SAggelos Economopoulos 	return EINVAL;
35858892ea20SAggelos Economopoulos }
35868892ea20SAggelos Economopoulos 
35878892ea20SAggelos Economopoulos static int
35888892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
35898892ea20SAggelos Economopoulos {
35908892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
35918892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
35928892ea20SAggelos Economopoulos 	int err = 0;
35938892ea20SAggelos Economopoulos 
3594b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3595b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
35968892ea20SAggelos Economopoulos 		return EINVAL;
3597b9a8961fSSepherosa Ziehau 
35988892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
35998892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
36002ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
360189d55360SSepherosa Ziehau 		mxge_close(sc, 0);
36028892ea20SAggelos Economopoulos 		err = mxge_open(sc);
36038892ea20SAggelos Economopoulos 		if (err != 0) {
36048892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
360589d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3606b9a8961fSSepherosa Ziehau 			mxge_open(sc);
36078892ea20SAggelos Economopoulos 		}
36088892ea20SAggelos Economopoulos 	}
36098892ea20SAggelos Economopoulos 	return err;
36108892ea20SAggelos Economopoulos }
36118892ea20SAggelos Economopoulos 
36128892ea20SAggelos Economopoulos static void
36138892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
36148892ea20SAggelos Economopoulos {
36158892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
36168892ea20SAggelos Economopoulos 
36178892ea20SAggelos Economopoulos 
36188892ea20SAggelos Economopoulos 	if (sc == NULL)
36198892ea20SAggelos Economopoulos 		return;
36208892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
362189d55360SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
36228892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
362389d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
36248892ea20SAggelos Economopoulos }
36258892ea20SAggelos Economopoulos 
36268892ea20SAggelos Economopoulos static int
362789d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
362889d55360SSepherosa Ziehau     struct ucred *cr __unused)
36298892ea20SAggelos Economopoulos {
36308892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
36318892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
36328892ea20SAggelos Economopoulos 	int err, mask;
36338892ea20SAggelos Economopoulos 
36348892ea20SAggelos Economopoulos 	err = 0;
363523811d63SAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
36368892ea20SAggelos Economopoulos 	switch (command) {
36378892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
36388892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
36398892ea20SAggelos Economopoulos 		break;
36408892ea20SAggelos Economopoulos 
36418892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
36428892ea20SAggelos Economopoulos 		if (sc->dying) {
36438892ea20SAggelos Economopoulos 			return EINVAL;
36448892ea20SAggelos Economopoulos 		}
36458892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
36462ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
36478892ea20SAggelos Economopoulos 				err = mxge_open(sc);
36488892ea20SAggelos Economopoulos 			} else {
36498892ea20SAggelos Economopoulos 				/* take care of promis can allmulti
36508892ea20SAggelos Economopoulos 				   flag chages */
36518892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
36528892ea20SAggelos Economopoulos 						    ifp->if_flags & IFF_PROMISC);
36538892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
36548892ea20SAggelos Economopoulos 			}
36558892ea20SAggelos Economopoulos 		} else {
36562ab1b8a9SAggelos Economopoulos 			if (ifp->if_flags & IFF_RUNNING) {
365789d55360SSepherosa Ziehau 				mxge_close(sc, 0);
36588892ea20SAggelos Economopoulos 			}
36598892ea20SAggelos Economopoulos 		}
36608892ea20SAggelos Economopoulos 		break;
36618892ea20SAggelos Economopoulos 
36628892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
36638892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
36648892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
36658892ea20SAggelos Economopoulos 		break;
36668892ea20SAggelos Economopoulos 
36678892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
36688892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
36698892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
367089d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
367189d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
367289d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
36738892ea20SAggelos Economopoulos 			else
367489d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
36758892ea20SAggelos Economopoulos 		}
367689d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
367789d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
367889d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
367989d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
368089d55360SSepherosa Ziehau 			else
368189d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
368289d55360SSepherosa Ziehau 		}
368389d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
368489d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
36858892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
36868892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
36878892ea20SAggelos Economopoulos 		break;
36888892ea20SAggelos Economopoulos 
36898892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
369089d55360SSepherosa Ziehau 		mxge_media_probe(sc);
36918892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
36928892ea20SAggelos Economopoulos 				    &sc->media, command);
36938892ea20SAggelos Economopoulos 		break;
36948892ea20SAggelos Economopoulos 
36958892ea20SAggelos Economopoulos 	default:
369689d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
369789d55360SSepherosa Ziehau 		break;
36988892ea20SAggelos Economopoulos 	}
36998892ea20SAggelos Economopoulos 	return err;
37008892ea20SAggelos Economopoulos }
37018892ea20SAggelos Economopoulos 
37028892ea20SAggelos Economopoulos static void
37038892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
37048892ea20SAggelos Economopoulos {
37057cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
37067cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
37077cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
37088892ea20SAggelos Economopoulos 
37097cc92483SSepherosa Ziehau 	/* XXX */
37108892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
37118892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
37127cc92483SSepherosa Ziehau 
37138892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
37148892ea20SAggelos Economopoulos 
371589d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
37167cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
37177cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
37187cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
37197cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
372089d55360SSepherosa Ziehau }
37218892ea20SAggelos Economopoulos 
37228892ea20SAggelos Economopoulos static void
37238892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
37248892ea20SAggelos Economopoulos {
37258892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37268892ea20SAggelos Economopoulos 	int i;
37278892ea20SAggelos Economopoulos 
37288892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
37298892ea20SAggelos Economopoulos 		return;
37308892ea20SAggelos Economopoulos 
37318892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37328892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37338892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
37348892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
37358892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
37368892ea20SAggelos Economopoulos 		}
37378892ea20SAggelos Economopoulos 		if (ss->rx_done.entry != NULL) {
37388892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->rx_done.dma);
37398892ea20SAggelos Economopoulos 			ss->rx_done.entry = NULL;
37408892ea20SAggelos Economopoulos 		}
37418892ea20SAggelos Economopoulos 	}
37426c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
37438892ea20SAggelos Economopoulos 	sc->ss = NULL;
37448892ea20SAggelos Economopoulos }
37458892ea20SAggelos Economopoulos 
37468892ea20SAggelos Economopoulos static int
37478892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
37488892ea20SAggelos Economopoulos {
37498892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
37508892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37518892ea20SAggelos Economopoulos 	size_t bytes;
37528892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
37538892ea20SAggelos Economopoulos 
37548892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37558892ea20SAggelos Economopoulos 	if (err != 0) {
37568892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37578892ea20SAggelos Economopoulos 		return err;
37588892ea20SAggelos Economopoulos 	}
37598892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
37608892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37618892ea20SAggelos Economopoulos 
37628892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
37637cc92483SSepherosa Ziehau 	sc->ss = kmalloc(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
37647cc92483SSepherosa Ziehau 
37658892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37668892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37678892ea20SAggelos Economopoulos 
37688892ea20SAggelos Economopoulos 		ss->sc = sc;
37698892ea20SAggelos Economopoulos 
37707cc92483SSepherosa Ziehau 		/*
37717cc92483SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queues
37727cc92483SSepherosa Ziehau 		 */
37738892ea20SAggelos Economopoulos 		bytes = max_intr_slots * sizeof(*ss->rx_done.entry);
37748892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
3775798c3369SSepherosa Ziehau 		if (err != 0) {
3776798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3777798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3778798c3369SSepherosa Ziehau 			return err;
3779798c3369SSepherosa Ziehau 		}
37807cc92483SSepherosa Ziehau 		ss->rx_done.entry = ss->rx_done.dma.dmem_addr;
37818892ea20SAggelos Economopoulos 
37828892ea20SAggelos Economopoulos 		/*
37837cc92483SSepherosa Ziehau 		 * Allocate the per-slice firmware stats; stats
37848892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
37858892ea20SAggelos Economopoulos 		 * slice for now
37868892ea20SAggelos Economopoulos 		 */
37878892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
37888892ea20SAggelos Economopoulos 		if (i > 0)
37898892ea20SAggelos Economopoulos 			continue;
37908892ea20SAggelos Economopoulos #endif
37918892ea20SAggelos Economopoulos 
37928892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
37938892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37948892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3795798c3369SSepherosa Ziehau 		if (err != 0) {
3796798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3797798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3798798c3369SSepherosa Ziehau 			return err;
3799798c3369SSepherosa Ziehau 		}
38007cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
38018892ea20SAggelos Economopoulos 	}
38027cc92483SSepherosa Ziehau 	return 0;
38038892ea20SAggelos Economopoulos }
38048892ea20SAggelos Economopoulos 
38058892ea20SAggelos Economopoulos static void
38068892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
38078892ea20SAggelos Economopoulos {
38088892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3809c7431c78SSepherosa Ziehau 	const char *old_fw;
38108892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
38118892ea20SAggelos Economopoulos 
38128892ea20SAggelos Economopoulos 	sc->num_slices = 1;
38137cc92483SSepherosa Ziehau 
38148892ea20SAggelos Economopoulos 	/*
38157cc92483SSepherosa Ziehau 	 * XXX
38167cc92483SSepherosa Ziehau 	 *
38177cc92483SSepherosa Ziehau 	 * Don't enable multiple slices if they are not enabled,
38188892ea20SAggelos Economopoulos 	 * or if this is not an SMP system
38198892ea20SAggelos Economopoulos 	 */
3820b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
38218892ea20SAggelos Economopoulos 		return;
38228892ea20SAggelos Economopoulos 
38238892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
38248892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
38258892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
38268892ea20SAggelos Economopoulos 		return;
38278892ea20SAggelos Economopoulos 
38288892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
38298892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
38308892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
38318892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
38328892ea20SAggelos Economopoulos 	else
38338892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
38348892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
38358892ea20SAggelos Economopoulos 	if (status != 0) {
38368892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
38378892ea20SAggelos Economopoulos 		return;
38388892ea20SAggelos Economopoulos 	}
38398892ea20SAggelos Economopoulos 
38408892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
38418892ea20SAggelos Economopoulos 	   is alive */
38428892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
38438892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38448892ea20SAggelos Economopoulos 	if (status != 0) {
38458892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
38468892ea20SAggelos Economopoulos 		goto abort_with_fw;
38478892ea20SAggelos Economopoulos 	}
38488892ea20SAggelos Economopoulos 
38498892ea20SAggelos Economopoulos 	/* get rx ring size */
38508892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38518892ea20SAggelos Economopoulos 	if (status != 0) {
38528892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38538892ea20SAggelos Economopoulos 		goto abort_with_fw;
38548892ea20SAggelos Economopoulos 	}
38558892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38568892ea20SAggelos Economopoulos 
38578892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
38588892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38598892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38608892ea20SAggelos Economopoulos 	if (status != 0) {
38618892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38628892ea20SAggelos Economopoulos 		goto abort_with_fw;
38638892ea20SAggelos Economopoulos 	}
38648892ea20SAggelos Economopoulos 
38658892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
38668892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38678892ea20SAggelos Economopoulos 	if (status != 0) {
38688892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38698892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38708892ea20SAggelos Economopoulos 		goto abort_with_fw;
38718892ea20SAggelos Economopoulos 	}
38728892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
38738892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
38748892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
38758892ea20SAggelos Economopoulos 
38768892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
38778892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
3878ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
3879ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
38808892ea20SAggelos Economopoulos 	} else {
38818892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
38828892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
38838892ea20SAggelos Economopoulos 	}
38848892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
38858892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
38868892ea20SAggelos Economopoulos 		sc->num_slices--;
38878892ea20SAggelos Economopoulos 
38887cc92483SSepherosa Ziehau 	if (bootverbose)
38898892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
38908892ea20SAggelos Economopoulos 			      sc->num_slices);
38918892ea20SAggelos Economopoulos 
38928892ea20SAggelos Economopoulos 	return;
38938892ea20SAggelos Economopoulos 
38948892ea20SAggelos Economopoulos abort_with_fw:
38958892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
38968892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
38978892ea20SAggelos Economopoulos }
38988892ea20SAggelos Economopoulos 
3899a26af990SSepherosa Ziehau #if 0
39008892ea20SAggelos Economopoulos static int
39018892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
39028892ea20SAggelos Economopoulos {
39038892ea20SAggelos Economopoulos 	size_t bytes;
39048892ea20SAggelos Economopoulos 	int count, err, i, rid;
39058892ea20SAggelos Economopoulos 
39068892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
39078892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
39088892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
39098892ea20SAggelos Economopoulos 
39108892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
39118892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
39128892ea20SAggelos Economopoulos 		return ENXIO;
39138892ea20SAggelos Economopoulos 	}
39148892ea20SAggelos Economopoulos 
39158892ea20SAggelos Economopoulos 	count = sc->num_slices;
39168892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
39178892ea20SAggelos Economopoulos 	if (err != 0) {
39188892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39198892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
39208892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
39218892ea20SAggelos Economopoulos 	}
39228892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
39238892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39248892ea20SAggelos Economopoulos 			      count, sc->num_slices);
39258892ea20SAggelos Economopoulos 		device_printf(sc->dev,
39268892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
39278892ea20SAggelos Economopoulos 			      count);
39288892ea20SAggelos Economopoulos 		err = ENOSPC;
39298892ea20SAggelos Economopoulos 		goto abort_with_msix;
39308892ea20SAggelos Economopoulos 	}
39318892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
3932d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39338892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
39348892ea20SAggelos Economopoulos 		err = ENOMEM;
39358892ea20SAggelos Economopoulos 		goto abort_with_msix;
39368892ea20SAggelos Economopoulos 	}
39378892ea20SAggelos Economopoulos 
39388892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39398892ea20SAggelos Economopoulos 		rid = i + 1;
39408892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39418892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
39428892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
39438892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
39448892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
39458892ea20SAggelos Economopoulos 				      " for message %d\n", i);
39468892ea20SAggelos Economopoulos 			err = ENXIO;
39478892ea20SAggelos Economopoulos 			goto abort_with_res;
39488892ea20SAggelos Economopoulos 		}
39498892ea20SAggelos Economopoulos 	}
39508892ea20SAggelos Economopoulos 
39518892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
3952d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39538892ea20SAggelos Economopoulos 
39548892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39558892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39567d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
39577d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
39582e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
39598892ea20SAggelos Economopoulos 		if (err != 0) {
39608892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
39618892ea20SAggelos Economopoulos 				      "message %d\n", i);
39628892ea20SAggelos Economopoulos 			goto abort_with_intr;
39638892ea20SAggelos Economopoulos 		}
39648892ea20SAggelos Economopoulos 	}
39658892ea20SAggelos Economopoulos 
39667cc92483SSepherosa Ziehau 	if (bootverbose) {
39678892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
39688892ea20SAggelos Economopoulos 			      sc->num_slices);
39698892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
39706c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39716c348da6SAggelos Economopoulos 		kprintf("\n");
39728892ea20SAggelos Economopoulos 	}
39738892ea20SAggelos Economopoulos 	return (0);
39748892ea20SAggelos Economopoulos 
39758892ea20SAggelos Economopoulos abort_with_intr:
39768892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39778892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39788892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39798892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39808892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39818892ea20SAggelos Economopoulos 		}
39828892ea20SAggelos Economopoulos 	}
3983d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39848892ea20SAggelos Economopoulos 
39858892ea20SAggelos Economopoulos 
39868892ea20SAggelos Economopoulos abort_with_res:
39878892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39888892ea20SAggelos Economopoulos 		rid = i + 1;
39898892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39908892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39918892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39928892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39938892ea20SAggelos Economopoulos 	}
3994d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39958892ea20SAggelos Economopoulos 
39968892ea20SAggelos Economopoulos 
39978892ea20SAggelos Economopoulos abort_with_msix:
39988892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39998892ea20SAggelos Economopoulos 
40008892ea20SAggelos Economopoulos abort_with_msix_table:
40018892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40028892ea20SAggelos Economopoulos 			     sc->msix_table_res);
40038892ea20SAggelos Economopoulos 
40048892ea20SAggelos Economopoulos 	return err;
40058892ea20SAggelos Economopoulos }
4006a26af990SSepherosa Ziehau #endif
40078892ea20SAggelos Economopoulos 
40088892ea20SAggelos Economopoulos static int
40098892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
40108892ea20SAggelos Economopoulos {
401189d55360SSepherosa Ziehau 	u_int irq_flags;
401251c70c94SSascha Wildner 
40137cc92483SSepherosa Ziehau 	sc->irq_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
40147cc92483SSepherosa Ziehau 	    &sc->irq_rid, &irq_flags);
401589d55360SSepherosa Ziehau 
401689d55360SSepherosa Ziehau 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
401789d55360SSepherosa Ziehau 	    &sc->irq_rid, irq_flags);
40188892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
40198892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
40208892ea20SAggelos Economopoulos 		return ENXIO;
40218892ea20SAggelos Economopoulos 	}
402289d55360SSepherosa Ziehau 
4023798c3369SSepherosa Ziehau 	return bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE,
40247cc92483SSepherosa Ziehau 	    mxge_intr, &sc->ss[0], &sc->ih, sc->ifp->if_serializer);
40258892ea20SAggelos Economopoulos }
40268892ea20SAggelos Economopoulos 
4027a26af990SSepherosa Ziehau #if 0
40288892ea20SAggelos Economopoulos static void
40298892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
40308892ea20SAggelos Economopoulos {
40318892ea20SAggelos Economopoulos 	int i, rid;
40328892ea20SAggelos Economopoulos 
40338892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40348892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
40358892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40368892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
40378892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
40388892ea20SAggelos Economopoulos 		}
40398892ea20SAggelos Economopoulos 	}
4040d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
40418892ea20SAggelos Economopoulos 
40428892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40438892ea20SAggelos Economopoulos 		rid = i + 1;
40448892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
40458892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40468892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
40478892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
40488892ea20SAggelos Economopoulos 	}
4049d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
40508892ea20SAggelos Economopoulos 
40518892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40528892ea20SAggelos Economopoulos 			     sc->msix_table_res);
40538892ea20SAggelos Economopoulos 
40548892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
40558892ea20SAggelos Economopoulos 	return;
40568892ea20SAggelos Economopoulos }
4057a26af990SSepherosa Ziehau #endif
40588892ea20SAggelos Economopoulos 
40598892ea20SAggelos Economopoulos static int
40608892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
40618892ea20SAggelos Economopoulos {
4062a26af990SSepherosa Ziehau #if 0
40638892ea20SAggelos Economopoulos 	int err;
40648892ea20SAggelos Economopoulos 
40658892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
40668892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40678892ea20SAggelos Economopoulos 	else
40688892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
40698892ea20SAggelos Economopoulos 
40708892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
40718892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
40728892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40738892ea20SAggelos Economopoulos 	}
40748892ea20SAggelos Economopoulos 	return err;
4075a26af990SSepherosa Ziehau #else
4076a26af990SSepherosa Ziehau 	return mxge_add_single_irq(sc);
4077a26af990SSepherosa Ziehau #endif
40788892ea20SAggelos Economopoulos }
40798892ea20SAggelos Economopoulos 
40808892ea20SAggelos Economopoulos static int
40818892ea20SAggelos Economopoulos mxge_attach(device_t dev)
40828892ea20SAggelos Economopoulos {
40838892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4084137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
40858892ea20SAggelos Economopoulos 	int err, rid;
40868892ea20SAggelos Economopoulos 
4087f0115d64SAggelos Economopoulos 	/*
40887cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4089f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4090f0115d64SAggelos Economopoulos 	 */
4091f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
40928892ea20SAggelos Economopoulos 	sc->dev = dev;
40937cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
4094798c3369SSepherosa Ziehau 	ifmedia_init(&sc->media, 0, mxge_media_change, mxge_media_status);
40957cc92483SSepherosa Ziehau 
40968892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
40978892ea20SAggelos Economopoulos 
40988892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
40998892ea20SAggelos Economopoulos 				 1,			/* alignment */
41008892ea20SAggelos Economopoulos 				 0,			/* boundary */
41018892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
41028892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
41038892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
41047cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
41057cc92483SSepherosa Ziehau 				 0, 			/* num segs */
41067cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
41078892ea20SAggelos Economopoulos 				 0,			/* flags */
41088892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
41098892ea20SAggelos Economopoulos 	if (err != 0) {
4110798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4111798c3369SSepherosa Ziehau 		goto failed;
41128892ea20SAggelos Economopoulos 	}
41138892ea20SAggelos Economopoulos 
4114e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
41158892ea20SAggelos Economopoulos 
41168892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
41178892ea20SAggelos Economopoulos 
41187cc92483SSepherosa Ziehau 	/*
41197cc92483SSepherosa Ziehau 	 * Map the board into the kernel
41207cc92483SSepherosa Ziehau 	 */
41218892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
41227cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
41237cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
41248892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
41258892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
41268892ea20SAggelos Economopoulos 		err = ENXIO;
4127798c3369SSepherosa Ziehau 		goto failed;
41288892ea20SAggelos Economopoulos 	}
41297cc92483SSepherosa Ziehau 
41308892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
41318892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
41328892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
41338892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
41348892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
41358892ea20SAggelos Economopoulos 		err = ENXIO;
4136798c3369SSepherosa Ziehau 		goto failed;
41378892ea20SAggelos Economopoulos 	}
41388892ea20SAggelos Economopoulos 
41397cc92483SSepherosa Ziehau 	/*
41407cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
41417cc92483SSepherosa Ziehau 	 * lanai SRAM
41427cc92483SSepherosa Ziehau 	 */
41438892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
41448892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
41458892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
41468892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
41477cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
41488892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4149798c3369SSepherosa Ziehau 	if (err != 0) {
4150798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4151798c3369SSepherosa Ziehau 		goto failed;
4152798c3369SSepherosa Ziehau 	}
41538892ea20SAggelos Economopoulos 
41547cc92483SSepherosa Ziehau 	/*
41557cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
41567cc92483SSepherosa Ziehau 	 */
41578892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
41588892ea20SAggelos Economopoulos 
41597cc92483SSepherosa Ziehau 	/*
41607cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
41617cc92483SSepherosa Ziehau 	 */
41627cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4163798c3369SSepherosa Ziehau 	if (err != 0) {
4164798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4165798c3369SSepherosa Ziehau 		goto failed;
4166798c3369SSepherosa Ziehau 	}
41677cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
41687cc92483SSepherosa Ziehau 
41698892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4170798c3369SSepherosa Ziehau 	if (err != 0) {
4171798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4172798c3369SSepherosa Ziehau 		goto failed;
4173798c3369SSepherosa Ziehau 	}
41748892ea20SAggelos Economopoulos 
41758892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4176798c3369SSepherosa Ziehau 	if (err != 0) {
4177798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4178798c3369SSepherosa Ziehau 		goto failed;
4179798c3369SSepherosa Ziehau 	}
41808892ea20SAggelos Economopoulos 
41817cc92483SSepherosa Ziehau 	/* Select & load the firmware */
41828892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4183798c3369SSepherosa Ziehau 	if (err != 0) {
4184798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4185798c3369SSepherosa Ziehau 		goto failed;
4186798c3369SSepherosa Ziehau 	}
41878892ea20SAggelos Economopoulos 
41888892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
41898892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4190798c3369SSepherosa Ziehau 	if (err != 0) {
4191798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4192798c3369SSepherosa Ziehau 		goto failed;
4193798c3369SSepherosa Ziehau 	}
41948892ea20SAggelos Economopoulos 
41958892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4196798c3369SSepherosa Ziehau 	if (err != 0) {
4197798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4198798c3369SSepherosa Ziehau 		goto failed;
4199798c3369SSepherosa Ziehau 	}
42008892ea20SAggelos Economopoulos 
42018892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
42028892ea20SAggelos Economopoulos 	if (err != 0) {
4203798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4204798c3369SSepherosa Ziehau 		goto failed;
42058892ea20SAggelos Economopoulos 	}
42068892ea20SAggelos Economopoulos 
42078892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
420889d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
42098892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
421089d55360SSepherosa Ziehau 
421189d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
421289d55360SSepherosa Ziehau #if 0
421389d55360SSepherosa Ziehau 	/* Well, its software, sigh */
421489d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
421589d55360SSepherosa Ziehau #endif
42168892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
421789d55360SSepherosa Ziehau 
42188892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
42198892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
422089d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
42218892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
42228892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
422389d55360SSepherosa Ziehau 	/* XXX watchdog */
422489d55360SSepherosa Ziehau 
4225820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4226820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4227820e213fSSepherosa Ziehau 
42288892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
422989d55360SSepherosa Ziehau 	mxge_media_init(sc);
42308892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
423189d55360SSepherosa Ziehau 
4232cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
423389d55360SSepherosa Ziehau 
4234b9a8961fSSepherosa Ziehau 	/*
4235b9a8961fSSepherosa Ziehau 	 * XXX
4236b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4237b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4238b9a8961fSSepherosa Ziehau 	 */
4239b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4240b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
424189d55360SSepherosa Ziehau 	sc->dying = 0;
424289d55360SSepherosa Ziehau 
4243369c353eSAggelos Economopoulos 	/* must come after ether_ifattach() */
4244369c353eSAggelos Economopoulos 	err = mxge_add_irq(sc);
4245369c353eSAggelos Economopoulos 	if (err != 0) {
4246798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4247798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4248798c3369SSepherosa Ziehau 		goto failed;
4249369c353eSAggelos Economopoulos 	}
425089d55360SSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->irq_res));
42518892ea20SAggelos Economopoulos 
42528892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
425389d55360SSepherosa Ziehau 
425489d55360SSepherosa Ziehau 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
42558892ea20SAggelos Economopoulos 	return 0;
42568892ea20SAggelos Economopoulos 
4257798c3369SSepherosa Ziehau failed:
4258798c3369SSepherosa Ziehau 	mxge_detach(dev);
42598892ea20SAggelos Economopoulos 	return err;
42608892ea20SAggelos Economopoulos }
42618892ea20SAggelos Economopoulos 
42628892ea20SAggelos Economopoulos static int
42638892ea20SAggelos Economopoulos mxge_detach(device_t dev)
42648892ea20SAggelos Economopoulos {
42658892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
42668892ea20SAggelos Economopoulos 
4267798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4268798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
4269798c3369SSepherosa Ziehau 
4270798c3369SSepherosa Ziehau 		lwkt_serialize_enter(ifp->if_serializer);
4271798c3369SSepherosa Ziehau 
42728892ea20SAggelos Economopoulos 		sc->dying = 1;
4273798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
427489d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4275e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4276798c3369SSepherosa Ziehau 
4277798c3369SSepherosa Ziehau 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
4278798c3369SSepherosa Ziehau 
4279798c3369SSepherosa Ziehau 		lwkt_serialize_exit(ifp->if_serializer);
4280e3dc37faSAggelos Economopoulos 
428189d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
428289d55360SSepherosa Ziehau 
4283798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4284798c3369SSepherosa Ziehau 	}
42858892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4286798c3369SSepherosa Ziehau 
4287798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4288798c3369SSepherosa Ziehau 	    sc->sram != NULL)
42898892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4290798c3369SSepherosa Ziehau 
42918892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
42928892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4293798c3369SSepherosa Ziehau 
4294798c3369SSepherosa Ziehau 	/* MUST after sysctls and rings are freed */
42958892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4296798c3369SSepherosa Ziehau 
4297798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
42988892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4299798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
43008892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4301798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
43028892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4303798c3369SSepherosa Ziehau 
4304798c3369SSepherosa Ziehau 	if (sc->irq_res != NULL) {
4305798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
4306798c3369SSepherosa Ziehau 		    sc->irq_res);
4307798c3369SSepherosa Ziehau 	}
4308798c3369SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_MSI)
4309798c3369SSepherosa Ziehau 		pci_release_msi(dev);
4310798c3369SSepherosa Ziehau 
4311798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4312798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4313798c3369SSepherosa Ziehau 		    sc->mem_res);
4314798c3369SSepherosa Ziehau 	}
4315798c3369SSepherosa Ziehau 
4316798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
43178892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4318798c3369SSepherosa Ziehau 
43198892ea20SAggelos Economopoulos 	return 0;
43208892ea20SAggelos Economopoulos }
43218892ea20SAggelos Economopoulos 
43228892ea20SAggelos Economopoulos static int
43238892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
43248892ea20SAggelos Economopoulos {
43258892ea20SAggelos Economopoulos 	return 0;
43268892ea20SAggelos Economopoulos }
4327