xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision fed54363)
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);
146ca8ca004SSepherosa Ziehau static void mxge_watchdog_reset(mxge_softc_t *sc);
147ca8ca004SSepherosa Ziehau static void mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice);
1488892ea20SAggelos Economopoulos 
1498892ea20SAggelos Economopoulos static int
1508892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1518892ea20SAggelos Economopoulos {
15224e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
15324e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
15424e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
15524e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1568892ea20SAggelos Economopoulos 
1578892ea20SAggelos Economopoulos 		switch (rev) {
1588892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1598892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1608892ea20SAggelos Economopoulos 			break;
1618892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1628892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1638892ea20SAggelos Economopoulos 			break;
1648892ea20SAggelos Economopoulos 		default:
1658892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
16624e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1678892ea20SAggelos Economopoulos 			break;
1688892ea20SAggelos Economopoulos 		}
1698892ea20SAggelos Economopoulos 		return 0;
1708892ea20SAggelos Economopoulos 	}
1718892ea20SAggelos Economopoulos 	return ENXIO;
1728892ea20SAggelos Economopoulos }
1738892ea20SAggelos Economopoulos 
1748892ea20SAggelos Economopoulos static void
1758892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1768892ea20SAggelos Economopoulos {
17789d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
1788892ea20SAggelos Economopoulos 	vm_offset_t len;
1798892ea20SAggelos Economopoulos 
1808892ea20SAggelos Economopoulos 	sc->wc = 1;
1818892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
18289d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
18389d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
1849eb279beSAggelos Economopoulos #endif
1858892ea20SAggelos Economopoulos }
1868892ea20SAggelos Economopoulos 
1878892ea20SAggelos Economopoulos static int
1887cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
1898892ea20SAggelos Economopoulos     bus_size_t alignment)
1908892ea20SAggelos Economopoulos {
1917cc92483SSepherosa Ziehau 	bus_size_t boundary;
1928892ea20SAggelos Economopoulos 	int err;
1938892ea20SAggelos Economopoulos 
1947cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
1958892ea20SAggelos Economopoulos 		boundary = 0;
1967cc92483SSepherosa Ziehau 	else
1978892ea20SAggelos Economopoulos 		boundary = 4096;
1988892ea20SAggelos Economopoulos 
1997cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
2007cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
2017cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2028892ea20SAggelos Economopoulos 	if (err != 0) {
2037cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2048892ea20SAggelos Economopoulos 		return err;
2058892ea20SAggelos Economopoulos 	}
2068892ea20SAggelos Economopoulos 	return 0;
2078892ea20SAggelos Economopoulos }
2088892ea20SAggelos Economopoulos 
2098892ea20SAggelos Economopoulos static void
2107cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2118892ea20SAggelos Economopoulos {
2127cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2137cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2147cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2158892ea20SAggelos Economopoulos }
2168892ea20SAggelos Economopoulos 
2178892ea20SAggelos Economopoulos /*
2188892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2198892ea20SAggelos Economopoulos  * SN=x\0
2208892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2218892ea20SAggelos Economopoulos  * PC=text\0
2228892ea20SAggelos Economopoulos  */
2238892ea20SAggelos Economopoulos static int
2248892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2258892ea20SAggelos Economopoulos {
226c7431c78SSepherosa Ziehau 	const char *ptr;
22789d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
22889d55360SSepherosa Ziehau 	char *endptr;
2298892ea20SAggelos Economopoulos 
2308892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2318892ea20SAggelos Economopoulos 	found_mac = 0;
23289d55360SSepherosa Ziehau 	found_sn2 = 0;
23389d55360SSepherosa Ziehau 	while (*ptr != '\0') {
23489d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
23589d55360SSepherosa Ziehau 			ptr += 4;
23689d55360SSepherosa Ziehau 			for (i = 0;;) {
23789d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
23889d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2398892ea20SAggelos Economopoulos 					goto abort;
24089d55360SSepherosa Ziehau 				ptr = endptr;
24189d55360SSepherosa Ziehau 				if (++i == 6)
24289d55360SSepherosa Ziehau 					break;
24389d55360SSepherosa Ziehau 				if (*ptr++ != ':')
24489d55360SSepherosa Ziehau 					goto abort;
24589d55360SSepherosa Ziehau 			}
2468892ea20SAggelos Economopoulos 			found_mac = 1;
24789d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2488892ea20SAggelos Economopoulos 			ptr += 3;
24989d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
25089d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
25189d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2528892ea20SAggelos Economopoulos 			ptr += 3;
25389d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25489d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
25589d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
25689d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
25789d55360SSepherosa Ziehau 			ptr += 4;
25889d55360SSepherosa Ziehau 			found_sn2 = 1;
25989d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
26089d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2618892ea20SAggelos Economopoulos 		}
26289d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2638892ea20SAggelos Economopoulos 	}
2648892ea20SAggelos Economopoulos 
2658892ea20SAggelos Economopoulos 	if (found_mac)
2668892ea20SAggelos Economopoulos 		return 0;
2678892ea20SAggelos Economopoulos 
2688892ea20SAggelos Economopoulos abort:
2698892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2708892ea20SAggelos Economopoulos 	return ENXIO;
2718892ea20SAggelos Economopoulos }
2728892ea20SAggelos Economopoulos 
27389d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
27489d55360SSepherosa Ziehau 
2758892ea20SAggelos Economopoulos static void
2768892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
2778892ea20SAggelos Economopoulos {
2788892ea20SAggelos Economopoulos 	uint32_t val;
2798892ea20SAggelos Economopoulos 	unsigned long base, off;
2808892ea20SAggelos Economopoulos 	char *va, *cfgptr;
2818892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
2828892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
2838892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
2848892ea20SAggelos Economopoulos 	uint32_t *ptr32;
2858892ea20SAggelos Economopoulos 
2868892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
2878892ea20SAggelos Economopoulos 		return;
2888892ea20SAggelos Economopoulos 
2898892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
2908892ea20SAggelos Economopoulos 	if (pdev == NULL) {
2918892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
2928892ea20SAggelos Economopoulos 		return;
2938892ea20SAggelos Economopoulos 	}
2948892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
2958892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
2968892ea20SAggelos Economopoulos 
2978892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
2988892ea20SAggelos Economopoulos 		return;
2998892ea20SAggelos Economopoulos 
3008892ea20SAggelos Economopoulos 	base = 0;
3018892ea20SAggelos Economopoulos 
3028892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3038892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3048892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3058892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3068892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3078892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3088892ea20SAggelos Economopoulos 		if (mcp55 &&
3098892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3108892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3118892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3128892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3138892ea20SAggelos Economopoulos 		}
3148892ea20SAggelos Economopoulos 	}
3158892ea20SAggelos Economopoulos 	if (!base)
3168892ea20SAggelos Economopoulos 		return;
3178892ea20SAggelos Economopoulos 
3187cc92483SSepherosa Ziehau 	/*
3197cc92483SSepherosa Ziehau 	 * XXXX
3207cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3217cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3227cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3237cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3242f47b54fSSepherosa Ziehau 	 * DragonFly grows support for extended pcie config space access.
3258892ea20SAggelos Economopoulos 	 */
3268892ea20SAggelos Economopoulos #if 0
3277cc92483SSepherosa Ziehau 	/*
3287cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3297cc92483SSepherosa Ziehau 	 * config space
3307cc92483SSepherosa Ziehau 	 */
3318892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3328892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3338892ea20SAggelos Economopoulos 		val |= 0x40;
3348892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3358892ea20SAggelos Economopoulos 		return;
3368892ea20SAggelos Economopoulos 	}
3378892ea20SAggelos Economopoulos #endif
3387cc92483SSepherosa Ziehau 	/*
3397cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3408892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3418892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3428892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3438892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3448892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3458892ea20SAggelos Economopoulos 	 */
3468892ea20SAggelos Economopoulos 
3478892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3488892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3498892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3508892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3518892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3528892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3538892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3548892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3558892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3568892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3578892ea20SAggelos Economopoulos 
3587cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3597cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3608892ea20SAggelos Economopoulos 
3618892ea20SAggelos Economopoulos 	/* map it into the kernel */
3628892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3638892ea20SAggelos Economopoulos 	if (va == NULL) {
3648892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3658892ea20SAggelos Economopoulos 		return;
3668892ea20SAggelos Economopoulos 	}
3678892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3688892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3698892ea20SAggelos Economopoulos 
3708892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3718892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3728892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3738892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3748892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3758892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3768892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3778892ea20SAggelos Economopoulos 		return;
3788892ea20SAggelos Economopoulos 	}
3798892ea20SAggelos Economopoulos 
3808892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
3818892ea20SAggelos Economopoulos 	val = *ptr32;
3828892ea20SAggelos Economopoulos 
3838892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
3848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
3858892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3868892ea20SAggelos Economopoulos 		return;
3878892ea20SAggelos Economopoulos 	}
3888892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
3898892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3907cc92483SSepherosa Ziehau 	if (bootverbose) {
3917cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
3927cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
3938892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
3947cc92483SSepherosa Ziehau 	}
3958892ea20SAggelos Economopoulos }
39689d55360SSepherosa Ziehau 
39789d55360SSepherosa Ziehau #else	/* __i386__ || __x86_64__ */
39889d55360SSepherosa Ziehau 
3998892ea20SAggelos Economopoulos static void
4008892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
4018892ea20SAggelos Economopoulos {
4027cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4038892ea20SAggelos Economopoulos }
4048892ea20SAggelos Economopoulos 
40589d55360SSepherosa Ziehau #endif
4068892ea20SAggelos Economopoulos 
4078892ea20SAggelos Economopoulos static int
4088892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4098892ea20SAggelos Economopoulos {
4108892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4117cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4128892ea20SAggelos Economopoulos 	int status;
4138892ea20SAggelos Economopoulos 	uint32_t len;
4147cc92483SSepherosa Ziehau 	const char *test = " ";
4158892ea20SAggelos Economopoulos 
4167cc92483SSepherosa Ziehau 	/*
4177cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4188892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4198892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4208892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4218892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4228892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4238892ea20SAggelos Economopoulos 	 * transfers took to complete.
4248892ea20SAggelos Economopoulos 	 */
4258892ea20SAggelos Economopoulos 
4268892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4278892ea20SAggelos Economopoulos 
4288892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4298892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4308892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4318892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4328892ea20SAggelos Economopoulos 	if (status != 0) {
4338892ea20SAggelos Economopoulos 		test = "read";
4348892ea20SAggelos Economopoulos 		goto abort;
4358892ea20SAggelos Economopoulos 	}
4367cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4377cc92483SSepherosa Ziehau 
4388892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4398892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4408892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4418892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4428892ea20SAggelos Economopoulos 	if (status != 0) {
4438892ea20SAggelos Economopoulos 		test = "write";
4448892ea20SAggelos Economopoulos 		goto abort;
4458892ea20SAggelos Economopoulos 	}
4467cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4478892ea20SAggelos Economopoulos 
4488892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4498892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4508892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4518892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4528892ea20SAggelos Economopoulos 	if (status != 0) {
4538892ea20SAggelos Economopoulos 		test = "read/write";
4548892ea20SAggelos Economopoulos 		goto abort;
4558892ea20SAggelos Economopoulos 	}
4568892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4578892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4588892ea20SAggelos Economopoulos 
4598892ea20SAggelos Economopoulos abort:
4607cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4618892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4628892ea20SAggelos Economopoulos 		    test, status);
4637cc92483SSepherosa Ziehau 	}
4648892ea20SAggelos Economopoulos 	return status;
4658892ea20SAggelos Economopoulos }
4668892ea20SAggelos Economopoulos 
4678892ea20SAggelos Economopoulos /*
4688892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4698892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4708892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4718892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4728892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4738892ea20SAggelos Economopoulos  *
4748892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4758892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4768892ea20SAggelos Economopoulos  *
4778892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
4788892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
4798892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
4808892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
4818892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
4828892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
4838892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
4848892ea20SAggelos Economopoulos  */
4858892ea20SAggelos Economopoulos static int
4868892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
4878892ea20SAggelos Economopoulos {
4888892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
4898892ea20SAggelos Economopoulos 	int reg, status;
4908892ea20SAggelos Economopoulos 	uint16_t pectl;
4918892ea20SAggelos Economopoulos 
4928892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
4937cc92483SSepherosa Ziehau 
4948892ea20SAggelos Economopoulos 	/*
4958892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
4968892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
4978892ea20SAggelos Economopoulos 	 */
4988892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
4998892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
5008892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
5017cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5028892ea20SAggelos Economopoulos 			    pectl);
5038892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5048892ea20SAggelos Economopoulos 		}
5058892ea20SAggelos Economopoulos 	}
5068892ea20SAggelos Economopoulos 
5078892ea20SAggelos Economopoulos 	/*
5087cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5098892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5108892ea20SAggelos Economopoulos 	 */
5118892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5128892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5137cc92483SSepherosa Ziehau 	if (status != 0)
5148892ea20SAggelos Economopoulos 		return status;
5158892ea20SAggelos Economopoulos 
5168892ea20SAggelos Economopoulos 	/*
5178892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5188892ea20SAggelos Economopoulos 	 */
5198892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5208892ea20SAggelos Economopoulos 
5218892ea20SAggelos Economopoulos 	/*
5228892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
52389d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5248892ea20SAggelos Economopoulos 	 */
52589d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
52689d55360SSepherosa Ziehau 		return 0;
5278892ea20SAggelos Economopoulos 
5288892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5298892ea20SAggelos Economopoulos 	if (status == 0)
5308892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5318892ea20SAggelos Economopoulos 
5328892ea20SAggelos Economopoulos 	if (status != E2BIG)
5338892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5347cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5358892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5368892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5377cc92483SSepherosa Ziehau 	}
5388892ea20SAggelos Economopoulos 	return status;
5398892ea20SAggelos Economopoulos }
5408892ea20SAggelos Economopoulos 
5418892ea20SAggelos Economopoulos static int
5428892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5438892ea20SAggelos Economopoulos {
5448892ea20SAggelos Economopoulos 	int aligned = 0;
54589d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5468892ea20SAggelos Economopoulos 
54789d55360SSepherosa Ziehau 	if (sc->throttle)
54889d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5498892ea20SAggelos Economopoulos 
55089d55360SSepherosa Ziehau 	if (force_firmware != 0) {
55189d55360SSepherosa Ziehau 		if (force_firmware == 1)
5528892ea20SAggelos Economopoulos 			aligned = 1;
5538892ea20SAggelos Economopoulos 		else
5548892ea20SAggelos Economopoulos 			aligned = 0;
5557cc92483SSepherosa Ziehau 		if (bootverbose) {
5568892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5578892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5588892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5597cc92483SSepherosa Ziehau 		}
5608892ea20SAggelos Economopoulos 		goto abort;
5618892ea20SAggelos Economopoulos 	}
5628892ea20SAggelos Economopoulos 
5637cc92483SSepherosa Ziehau 	/*
5647cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5657cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5667cc92483SSepherosa Ziehau 	 */
5678892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5687cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5697cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5708892ea20SAggelos Economopoulos 		aligned = 1;
5718892ea20SAggelos Economopoulos 		goto abort;
5728892ea20SAggelos Economopoulos 	}
5738892ea20SAggelos Economopoulos 
5747cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5758892ea20SAggelos Economopoulos 		return 0;
5768892ea20SAggelos Economopoulos 
5778892ea20SAggelos Economopoulos abort:
5788892ea20SAggelos Economopoulos 	if (aligned) {
5798892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
5808892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
5818892ea20SAggelos Economopoulos 	} else {
5828892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
5838892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
5848892ea20SAggelos Economopoulos 	}
5857cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
5868892ea20SAggelos Economopoulos }
5878892ea20SAggelos Economopoulos 
5888892ea20SAggelos Economopoulos static int
5898892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
5908892ea20SAggelos Economopoulos {
5918892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
5928a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware type: 0x%x\n",
5938892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
5948892ea20SAggelos Economopoulos 		return EIO;
5958892ea20SAggelos Economopoulos 	}
5968892ea20SAggelos Economopoulos 
5977cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
5987cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
5997cc92483SSepherosa Ziehau 	if (bootverbose)
6008a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "firmware id: %s\n", hdr->version);
6018892ea20SAggelos Economopoulos 
602b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6038892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6048892ea20SAggelos Economopoulos 
6057cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6067cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6078a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Found firmware version %s\n",
6088892ea20SAggelos Economopoulos 		    sc->fw_version);
6098a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Driver needs %d.%d\n",
6108892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6118892ea20SAggelos Economopoulos 		return EINVAL;
6128892ea20SAggelos Economopoulos 	}
6138892ea20SAggelos Economopoulos 	return 0;
6148892ea20SAggelos Economopoulos }
6158892ea20SAggelos Economopoulos 
6168892ea20SAggelos Economopoulos static void *
6178892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6188892ea20SAggelos Economopoulos {
6197cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6208892ea20SAggelos Economopoulos }
6218892ea20SAggelos Economopoulos 
6228892ea20SAggelos Economopoulos static void
6238892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6248892ea20SAggelos Economopoulos {
625d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6268892ea20SAggelos Economopoulos }
627d83c779aSSascha Wildner 
6288892ea20SAggelos Economopoulos static int
6298892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6308892ea20SAggelos Economopoulos {
631d83c779aSSascha Wildner 	z_stream zs;
632d83c779aSSascha Wildner 	char *inflate_buffer;
633d83c779aSSascha Wildner 	const struct firmware *fw;
6348892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6358892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6368892ea20SAggelos Economopoulos 	int status;
6378892ea20SAggelos Economopoulos 	unsigned int i;
63889d55360SSepherosa Ziehau 	char dummy;
6398892ea20SAggelos Economopoulos 	size_t fw_len;
6408892ea20SAggelos Economopoulos 
641d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6428892ea20SAggelos Economopoulos 	if (fw == NULL) {
6438a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Could not find firmware image %s\n",
6448892ea20SAggelos Economopoulos 		    sc->fw_name);
6458892ea20SAggelos Economopoulos 		return ENOENT;
6468892ea20SAggelos Economopoulos 	}
647d83c779aSSascha Wildner 
6487cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6498892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6508892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6518892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6528892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6538892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6548892ea20SAggelos Economopoulos 		status = EIO;
6558892ea20SAggelos Economopoulos 		goto abort_with_fw;
6568892ea20SAggelos Economopoulos 	}
6578892ea20SAggelos Economopoulos 
6587cc92483SSepherosa Ziehau 	/*
6597cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6607cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6617cc92483SSepherosa Ziehau 	 */
6628892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6637cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6648892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6658892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6668892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6678892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6688892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6698892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6708a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "zlib %d\n", status);
6718892ea20SAggelos Economopoulos 		status = EIO;
6728892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6738892ea20SAggelos Economopoulos 	}
674d83c779aSSascha Wildner 
6757cc92483SSepherosa Ziehau 	/* Check id */
6767cc92483SSepherosa Ziehau 	hdr_offset =
6777cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
6788892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
6798a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware file");
6808892ea20SAggelos Economopoulos 		status = EIO;
681d83c779aSSascha Wildner 		goto abort_with_buffer;
6828892ea20SAggelos Economopoulos 	}
683d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
6848892ea20SAggelos Economopoulos 
6858892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
6868892ea20SAggelos Economopoulos 	if (status != 0)
687d83c779aSSascha Wildner 		goto abort_with_buffer;
6888892ea20SAggelos Economopoulos 
6898892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
6908892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
6917cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
6928892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
6938892ea20SAggelos Economopoulos 		wmb();
69489d55360SSepherosa Ziehau 		dummy = *sc->sram;
6958892ea20SAggelos Economopoulos 		wmb();
6968892ea20SAggelos Economopoulos 	}
6978892ea20SAggelos Economopoulos 
6988892ea20SAggelos Economopoulos 	*limit = fw_len;
6998892ea20SAggelos Economopoulos 	status = 0;
7008892ea20SAggelos Economopoulos abort_with_buffer:
701d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7028892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7038892ea20SAggelos Economopoulos abort_with_fw:
704d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7058892ea20SAggelos Economopoulos 	return status;
7068892ea20SAggelos Economopoulos }
7078892ea20SAggelos Economopoulos 
7088892ea20SAggelos Economopoulos /*
7098892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7108892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7118892ea20SAggelos Economopoulos  */
7128892ea20SAggelos Economopoulos static void
7138892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7148892ea20SAggelos Economopoulos {
7158892ea20SAggelos Economopoulos 	char buf_bytes[72];
7168892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7178892ea20SAggelos Economopoulos 	volatile char *submit;
7188892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7198892ea20SAggelos Economopoulos 	int i;
7208892ea20SAggelos Economopoulos 
7218892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7228892ea20SAggelos Economopoulos 
7237cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7248892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7258892ea20SAggelos Economopoulos 	*confirm = 0;
7268892ea20SAggelos Economopoulos 	wmb();
7278892ea20SAggelos Economopoulos 
7287cc92483SSepherosa Ziehau 	/*
7297cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7307cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7317cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7328892ea20SAggelos Economopoulos 	 */
7337cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7347cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7358892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7368892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7378892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7387cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7397cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7408892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7418892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7428892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7438892ea20SAggelos Economopoulos 
7448892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7458892ea20SAggelos Economopoulos 
7468892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7478892ea20SAggelos Economopoulos 	wmb();
7488892ea20SAggelos Economopoulos 	DELAY(1000);
7498892ea20SAggelos Economopoulos 	wmb();
7508892ea20SAggelos Economopoulos 	i = 0;
7518892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7528892ea20SAggelos Economopoulos 		DELAY(1000);
7538892ea20SAggelos Economopoulos 		i++;
7548892ea20SAggelos Economopoulos 	}
7558892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7566ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7577cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7588892ea20SAggelos Economopoulos 	}
7598892ea20SAggelos Economopoulos }
7608892ea20SAggelos Economopoulos 
7618892ea20SAggelos Economopoulos static int
7628892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7638892ea20SAggelos Economopoulos {
7648892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7658892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7668892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7678892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7688892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7698892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7708892ea20SAggelos Economopoulos 
7716ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7728892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7738892ea20SAggelos Economopoulos 
7748892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7758892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7768892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
7778892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
7787cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7797cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7808892ea20SAggelos Economopoulos 
7818892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
7828892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
7832e8181d0SAggelos Economopoulos 
7848892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
7858892ea20SAggelos Economopoulos 	wmb();
7868892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
7878892ea20SAggelos Economopoulos 
7886ee6cba3SSepherosa Ziehau 	/*
7896ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
7906ee6cba3SSepherosa Ziehau 	 */
7918892ea20SAggelos Economopoulos 	err = EAGAIN;
7928892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
7938892ea20SAggelos Economopoulos 		wmb();
7948892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
7958892ea20SAggelos Economopoulos 		case 0:
7968892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
7978892ea20SAggelos Economopoulos 			err = 0;
7988892ea20SAggelos Economopoulos 			break;
7998892ea20SAggelos Economopoulos 		case 0xffffffff:
8008892ea20SAggelos Economopoulos 			DELAY(1000);
8018892ea20SAggelos Economopoulos 			break;
8028892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8038892ea20SAggelos Economopoulos 			err = ENOSYS;
8048892ea20SAggelos Economopoulos 			break;
8058892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8068892ea20SAggelos Economopoulos 			err = E2BIG;
8078892ea20SAggelos Economopoulos 			break;
8088892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8098892ea20SAggelos Economopoulos 			err = EBUSY;
8108892ea20SAggelos Economopoulos 			break;
81189d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
81289d55360SSepherosa Ziehau 			err = ENXIO;
81389d55360SSepherosa Ziehau 			break;
8148892ea20SAggelos Economopoulos 		default:
8156ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8168892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8178892ea20SAggelos Economopoulos 			err = ENXIO;
8188892ea20SAggelos Economopoulos 			break;
8198892ea20SAggelos Economopoulos 		}
8208892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8218892ea20SAggelos Economopoulos 			break;
8228892ea20SAggelos Economopoulos 	}
8236ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8246ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8258892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8266ee6cba3SSepherosa Ziehau 	}
8278892ea20SAggelos Economopoulos 	return err;
8288892ea20SAggelos Economopoulos }
8298892ea20SAggelos Economopoulos 
8308892ea20SAggelos Economopoulos static int
8318892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8328892ea20SAggelos Economopoulos {
8338892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8348892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8358892ea20SAggelos Economopoulos 	size_t hdr_offset;
8368892ea20SAggelos Economopoulos 	int status;
8378892ea20SAggelos Economopoulos 
8387cc92483SSepherosa Ziehau 	/*
8397cc92483SSepherosa Ziehau 	 * Find running firmware header
8407cc92483SSepherosa Ziehau 	 */
8417cc92483SSepherosa Ziehau 	hdr_offset =
8427cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8438892ea20SAggelos Economopoulos 
8448892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8458a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Running firmware has bad header offset "
8468a20b038SSepherosa Ziehau 		    "(%zu)\n", hdr_offset);
8478892ea20SAggelos Economopoulos 		return EIO;
8488892ea20SAggelos Economopoulos 	}
8498892ea20SAggelos Economopoulos 
8507cc92483SSepherosa Ziehau 	/*
8517cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8527cc92483SSepherosa Ziehau 	 * validate firmware
8537cc92483SSepherosa Ziehau 	 */
8547cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8558892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8567cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8578892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
858d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8598892ea20SAggelos Economopoulos 
8608892ea20SAggelos Economopoulos 	/*
8617cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8628892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8638892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8648892ea20SAggelos Economopoulos 	 */
8658892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8668892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8678892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8688a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Adopting fw %d.%d.%d: "
8698892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8707cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8718892ea20SAggelos Economopoulos 	}
8728892ea20SAggelos Economopoulos 
8738892ea20SAggelos Economopoulos 	return status;
8748892ea20SAggelos Economopoulos }
8758892ea20SAggelos Economopoulos 
8768892ea20SAggelos Economopoulos static int
8778892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
8788892ea20SAggelos Economopoulos {
8798892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
8808892ea20SAggelos Economopoulos 	volatile char *submit;
8818892ea20SAggelos Economopoulos 	char buf_bytes[72];
8828892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
8838892ea20SAggelos Economopoulos 	int status, i;
8848892ea20SAggelos Economopoulos 
8858892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
8868892ea20SAggelos Economopoulos 
8878892ea20SAggelos Economopoulos 	size = sc->sram_size;
8888892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
8898892ea20SAggelos Economopoulos 	if (status) {
8908892ea20SAggelos Economopoulos 		if (!adopt)
8918892ea20SAggelos Economopoulos 			return status;
8927cc92483SSepherosa Ziehau 
8937cc92483SSepherosa Ziehau 		/*
8947cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
8957cc92483SSepherosa Ziehau 		 * it is new enough
8967cc92483SSepherosa Ziehau 		 */
8978892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
8988892ea20SAggelos Economopoulos 		if (status) {
8998a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9008892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
9018892ea20SAggelos Economopoulos 			return status;
9028892ea20SAggelos Economopoulos 		}
9038a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Successfully adopted running firmware\n");
9047cc92483SSepherosa Ziehau 
9058892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9068a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9077cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9087cc92483SSepherosa Ziehau 			     "For optimal\n");
9098a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "performance consider loading "
9107cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9118892ea20SAggelos Economopoulos 		}
9128892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9138892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9148892ea20SAggelos Economopoulos 		return 0;
9158892ea20SAggelos Economopoulos 	}
9167cc92483SSepherosa Ziehau 
9177cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9188892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9198892ea20SAggelos Economopoulos 	*confirm = 0;
9208892ea20SAggelos Economopoulos 	wmb();
9217cc92483SSepherosa Ziehau 
9227cc92483SSepherosa Ziehau 	/*
9237cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9247cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9257cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9268892ea20SAggelos Economopoulos 	 */
9278892ea20SAggelos Economopoulos 
9287cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9297cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9308892ea20SAggelos Economopoulos 
9318892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9328892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9338892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9348892ea20SAggelos Economopoulos 
9357cc92483SSepherosa Ziehau 	/*
9367cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9377cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9387cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9398892ea20SAggelos Economopoulos 	 */
9408892ea20SAggelos Economopoulos 					/* where the code starts*/
9418892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9428892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9438892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9448892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9458892ea20SAggelos Economopoulos 
9468892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9478892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9488892ea20SAggelos Economopoulos 	wmb();
9498892ea20SAggelos Economopoulos 	DELAY(1000);
9508892ea20SAggelos Economopoulos 	wmb();
9518892ea20SAggelos Economopoulos 	i = 0;
9528892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9538892ea20SAggelos Economopoulos 		DELAY(1000*10);
9548892ea20SAggelos Economopoulos 		i++;
9558892ea20SAggelos Economopoulos 	}
9568892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9578a20b038SSepherosa Ziehau 		if_printf(sc->ifp,"handoff failed (%p = 0x%x)",
9588892ea20SAggelos Economopoulos 		    confirm, *confirm);
9598892ea20SAggelos Economopoulos 		return ENXIO;
9608892ea20SAggelos Economopoulos 	}
9618892ea20SAggelos Economopoulos 	return 0;
9628892ea20SAggelos Economopoulos }
9638892ea20SAggelos Economopoulos 
9648892ea20SAggelos Economopoulos static int
9658892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9668892ea20SAggelos Economopoulos {
9678892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9688892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9698892ea20SAggelos Economopoulos 
9707cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9717cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9727cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9737cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9748892ea20SAggelos Economopoulos }
9758892ea20SAggelos Economopoulos 
9768892ea20SAggelos Economopoulos static int
9778892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
9788892ea20SAggelos Economopoulos {
9798892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9808892ea20SAggelos Economopoulos 	int status;
9818892ea20SAggelos Economopoulos 
9828892ea20SAggelos Economopoulos 	if (pause)
9837cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
9848892ea20SAggelos Economopoulos 	else
9857cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
9868892ea20SAggelos Economopoulos 	if (status) {
9875a637e78SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set flow control mode\n");
9888892ea20SAggelos Economopoulos 		return ENXIO;
9898892ea20SAggelos Economopoulos 	}
9908892ea20SAggelos Economopoulos 	sc->pause = pause;
9918892ea20SAggelos Economopoulos 	return 0;
9928892ea20SAggelos Economopoulos }
9938892ea20SAggelos Economopoulos 
9948892ea20SAggelos Economopoulos static void
9958892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
9968892ea20SAggelos Economopoulos {
9978892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9988892ea20SAggelos Economopoulos 	int status;
9998892ea20SAggelos Economopoulos 
10008892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10018892ea20SAggelos Economopoulos 		promisc = 1;
10028892ea20SAggelos Economopoulos 
10038892ea20SAggelos Economopoulos 	if (promisc)
10047cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10058892ea20SAggelos Economopoulos 	else
10067cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10077cc92483SSepherosa Ziehau 	if (status)
1008af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set promisc mode\n");
10098892ea20SAggelos Economopoulos }
10108892ea20SAggelos Economopoulos 
10118892ea20SAggelos Economopoulos static void
10128892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10138892ea20SAggelos Economopoulos {
10148892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10158892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10168892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10178892ea20SAggelos Economopoulos 	int err;
10188892ea20SAggelos Economopoulos 
10198892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10208892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10218892ea20SAggelos Economopoulos 		return;
10228892ea20SAggelos Economopoulos 
10238892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
10248892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10258892ea20SAggelos Economopoulos 	if (err != 0) {
1026af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_ENABLE_ALLMULTI, "
10278892ea20SAggelos Economopoulos 		    "error status: %d\n", err);
10288892ea20SAggelos Economopoulos 		return;
10298892ea20SAggelos Economopoulos 	}
10308892ea20SAggelos Economopoulos 
10318892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10328892ea20SAggelos Economopoulos 		return;
10338892ea20SAggelos Economopoulos 
10347cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10357cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10368892ea20SAggelos Economopoulos 		return;
10378892ea20SAggelos Economopoulos 	}
10388892ea20SAggelos Economopoulos 
10397cc92483SSepherosa Ziehau 	/* Flush all the filters */
10407cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10417cc92483SSepherosa Ziehau 	if (err != 0) {
1042af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10437cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10447cc92483SSepherosa Ziehau 		return;
10457cc92483SSepherosa Ziehau 	}
10468892ea20SAggelos Economopoulos 
10477cc92483SSepherosa Ziehau 	/*
10487cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10497cc92483SSepherosa Ziehau 	 */
1050441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10518892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10528892ea20SAggelos Economopoulos 			continue;
10537cc92483SSepherosa Ziehau 
10548892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10558892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10568892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10578892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10588892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10598892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10608892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10618892ea20SAggelos Economopoulos 		if (err != 0) {
1062af85d4d5SSepherosa Ziehau 			if_printf(ifp, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
10637cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10647cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10658892ea20SAggelos Economopoulos 			return;
10668892ea20SAggelos Economopoulos 		}
10678892ea20SAggelos Economopoulos 	}
10687cc92483SSepherosa Ziehau 
10698892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10708892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10718892ea20SAggelos Economopoulos 	if (err != 0) {
1072af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_DISABLE_ALLMULTI, "
10737cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10748892ea20SAggelos Economopoulos 	}
10758892ea20SAggelos Economopoulos }
10768892ea20SAggelos Economopoulos 
107789d55360SSepherosa Ziehau #if 0
10788892ea20SAggelos Economopoulos static int
10798892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
10808892ea20SAggelos Economopoulos {
10818892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10828892ea20SAggelos Economopoulos 	int status;
10838892ea20SAggelos Economopoulos 
10848892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
10858892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10868892ea20SAggelos Economopoulos 
10878892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
10888892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
10898892ea20SAggelos Economopoulos 	cmd.data0 = 0;
10908892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
10918892ea20SAggelos Economopoulos 			       &cmd);
10928892ea20SAggelos Economopoulos 	if (status == 0)
10938892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10948892ea20SAggelos Economopoulos 
10958892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
10968892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
10978892ea20SAggelos Economopoulos }
109889d55360SSepherosa Ziehau #endif
10998892ea20SAggelos Economopoulos 
11008892ea20SAggelos Economopoulos static int
11018892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11028892ea20SAggelos Economopoulos {
11038892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11048892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11058892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11068892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11078892ea20SAggelos Economopoulos 	int slice, status;
11088892ea20SAggelos Economopoulos 
11097cc92483SSepherosa Ziehau 	/*
11107cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11117cc92483SSepherosa Ziehau 	 * is alive
11127cc92483SSepherosa Ziehau 	 */
11138892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11148892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11158892ea20SAggelos Economopoulos 	if (status != 0) {
11166ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11178892ea20SAggelos Economopoulos 		return ENXIO;
11188892ea20SAggelos Economopoulos 	}
11198892ea20SAggelos Economopoulos 
11208892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11218892ea20SAggelos Economopoulos 
11227cc92483SSepherosa Ziehau 	/* Set the intrq size */
11238892ea20SAggelos Economopoulos 	cmd.data0 = sc->rx_ring_size;
11248892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11258892ea20SAggelos Economopoulos 
11268892ea20SAggelos Economopoulos 	/*
11278892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11288892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11298892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11308892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11318892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11328892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11338892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11348892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11358892ea20SAggelos Economopoulos 	 */
11368892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11377cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11387cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11398892ea20SAggelos Economopoulos 		if (status != 0) {
11406ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11418892ea20SAggelos Economopoulos 			return status;
11428892ea20SAggelos Economopoulos 		}
11437cc92483SSepherosa Ziehau 
11448892ea20SAggelos Economopoulos 		/*
11458892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11468892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11478892ea20SAggelos Economopoulos 		 */
11488892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11498892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
11508892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
11518892ea20SAggelos Economopoulos 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11528892ea20SAggelos Economopoulos #endif
11537cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11548892ea20SAggelos Economopoulos 		if (status != 0) {
11556ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11568892ea20SAggelos Economopoulos 			return status;
11578892ea20SAggelos Economopoulos 		}
11588892ea20SAggelos Economopoulos 	}
11598892ea20SAggelos Economopoulos 
11608892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11618892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11628892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
11638892ea20SAggelos Economopoulos 			rx_done = &sc->ss[slice].rx_done;
11648892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
11657cc92483SSepherosa Ziehau 			cmd.data0 =
11667cc92483SSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(rx_done->dma.dmem_busaddr);
11677cc92483SSepherosa Ziehau 			cmd.data1 =
11687cc92483SSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(rx_done->dma.dmem_busaddr);
11698892ea20SAggelos Economopoulos 			cmd.data2 = slice;
11707cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
11718892ea20SAggelos Economopoulos 			    &cmd);
11728892ea20SAggelos Economopoulos 		}
11738892ea20SAggelos Economopoulos 	}
11748892ea20SAggelos Economopoulos 
11757cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
11767cc92483SSepherosa Ziehau 	    &cmd);
11778892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11788892ea20SAggelos Economopoulos 
11798892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11808892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11818892ea20SAggelos Economopoulos 
11827cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
11838892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
11847cc92483SSepherosa Ziehau 
11858892ea20SAggelos Economopoulos 	if (status != 0) {
11866ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
11878892ea20SAggelos Economopoulos 		return status;
11888892ea20SAggelos Economopoulos 	}
11898892ea20SAggelos Economopoulos 
11908892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11918892ea20SAggelos Economopoulos 
11927cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
11937cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
11948892ea20SAggelos Economopoulos 
11958892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
11968892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
11978892ea20SAggelos Economopoulos 
11988892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
11997cc92483SSepherosa Ziehau 
12007cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12018892ea20SAggelos Economopoulos 		ss->rx_done.idx = 0;
12028892ea20SAggelos Economopoulos 		ss->rx_done.cnt = 0;
12038892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12048892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12058892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12068892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12078892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12088892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12098892ea20SAggelos Economopoulos 		ss->rx_big.cnt = 0;
12108892ea20SAggelos Economopoulos 		ss->rx_small.cnt = 0;
12117cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12127cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12138892ea20SAggelos Economopoulos 	}
12148892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12157cc92483SSepherosa Ziehau 
12168892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12178892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12188892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12198892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12207cc92483SSepherosa Ziehau 
122189d55360SSepherosa Ziehau 	if (sc->throttle) {
122289d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12237cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12246ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
122589d55360SSepherosa Ziehau 	}
12268892ea20SAggelos Economopoulos 	return status;
12278892ea20SAggelos Economopoulos }
12288892ea20SAggelos Economopoulos 
12298892ea20SAggelos Economopoulos static int
123089d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
123189d55360SSepherosa Ziehau {
123289d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
123389d55360SSepherosa Ziehau 	mxge_softc_t *sc;
123489d55360SSepherosa Ziehau 	int err;
123589d55360SSepherosa Ziehau 	unsigned int throttle;
123689d55360SSepherosa Ziehau 
123789d55360SSepherosa Ziehau 	sc = arg1;
123889d55360SSepherosa Ziehau 	throttle = sc->throttle;
123989d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12405a637e78SSepherosa Ziehau 	if (err != 0)
124189d55360SSepherosa Ziehau 		return err;
124289d55360SSepherosa Ziehau 
124389d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
124489d55360SSepherosa Ziehau 		return 0;
124589d55360SSepherosa Ziehau 
124689d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
124789d55360SSepherosa Ziehau 		return EINVAL;
124889d55360SSepherosa Ziehau 
124989d55360SSepherosa Ziehau 	lwkt_serialize_enter(sc->ifp->if_serializer);
125089d55360SSepherosa Ziehau 
125189d55360SSepherosa Ziehau 	cmd.data0 = throttle;
125289d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
125389d55360SSepherosa Ziehau 	if (err == 0)
125489d55360SSepherosa Ziehau 		sc->throttle = throttle;
125589d55360SSepherosa Ziehau 
125689d55360SSepherosa Ziehau 	lwkt_serialize_exit(sc->ifp->if_serializer);
125789d55360SSepherosa Ziehau 	return err;
125889d55360SSepherosa Ziehau }
125989d55360SSepherosa Ziehau 
126089d55360SSepherosa Ziehau static int
12618892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
12628892ea20SAggelos Economopoulos {
12638892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12648892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
12658892ea20SAggelos Economopoulos 	int err;
12668892ea20SAggelos Economopoulos 
12678892ea20SAggelos Economopoulos 	sc = arg1;
12688892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
12698892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
12705a637e78SSepherosa Ziehau 	if (err != 0)
12718892ea20SAggelos Economopoulos 		return err;
12725a637e78SSepherosa Ziehau 
12738892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
12748892ea20SAggelos Economopoulos 		return 0;
12758892ea20SAggelos Economopoulos 
12768892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
12778892ea20SAggelos Economopoulos 		return EINVAL;
12788892ea20SAggelos Economopoulos 
12792e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
128089d55360SSepherosa Ziehau 
12818892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
12828892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
12838892ea20SAggelos Economopoulos 
12842e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
12858892ea20SAggelos Economopoulos 	return err;
12868892ea20SAggelos Economopoulos }
12878892ea20SAggelos Economopoulos 
12888892ea20SAggelos Economopoulos static int
12898892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
12908892ea20SAggelos Economopoulos {
12918892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12928892ea20SAggelos Economopoulos 	unsigned int enabled;
12938892ea20SAggelos Economopoulos 	int err;
12948892ea20SAggelos Economopoulos 
12958892ea20SAggelos Economopoulos 	sc = arg1;
12968892ea20SAggelos Economopoulos 	enabled = sc->pause;
12978892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
12985a637e78SSepherosa Ziehau 	if (err != 0)
12998892ea20SAggelos Economopoulos 		return err;
13005a637e78SSepherosa Ziehau 
13018892ea20SAggelos Economopoulos 	if (enabled == sc->pause)
13028892ea20SAggelos Economopoulos 		return 0;
13038892ea20SAggelos Economopoulos 
13042e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
13058892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
13062e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
13078892ea20SAggelos Economopoulos 
13088892ea20SAggelos Economopoulos 	return err;
13098892ea20SAggelos Economopoulos }
13108892ea20SAggelos Economopoulos 
13118892ea20SAggelos Economopoulos static int
13128892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13138892ea20SAggelos Economopoulos {
13148892ea20SAggelos Economopoulos 	int err;
13158892ea20SAggelos Economopoulos 
13168892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13178892ea20SAggelos Economopoulos 		return EFAULT;
13188892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13198892ea20SAggelos Economopoulos 	arg1 = NULL;
13208892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13218892ea20SAggelos Economopoulos 
13228892ea20SAggelos Economopoulos 	return err;
13238892ea20SAggelos Economopoulos }
13248892ea20SAggelos Economopoulos 
13258892ea20SAggelos Economopoulos static void
13268892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13278892ea20SAggelos Economopoulos {
1328798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13298892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13308892ea20SAggelos Economopoulos 		int slice;
13318892ea20SAggelos Economopoulos 
13328892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13338892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1334798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13358892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13368892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13378892ea20SAggelos Economopoulos 			}
1338798c3369SSepherosa Ziehau 		}
1339798c3369SSepherosa Ziehau 	}
1340798c3369SSepherosa Ziehau 
1341798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13428892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13438892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1344798c3369SSepherosa Ziehau 	}
1345798c3369SSepherosa Ziehau 
1346798c3369SSepherosa Ziehau 	if (sc->sysctl_tree != NULL) {
1347bbac37fbSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1348bbac37fbSAggelos Economopoulos 		sc->sysctl_tree = NULL;
13498892ea20SAggelos Economopoulos 	}
1350798c3369SSepherosa Ziehau }
13518892ea20SAggelos Economopoulos 
13528892ea20SAggelos Economopoulos static void
13538892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13548892ea20SAggelos Economopoulos {
13558892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13568892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13578892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13588892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13598892ea20SAggelos Economopoulos 	int slice;
13608892ea20SAggelos Economopoulos 	char slice_num[8];
13618892ea20SAggelos Economopoulos 
1362b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1363b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1364b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
13657cc92483SSepherosa Ziehau 	    OID_AUTO, device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "");
1366b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1367b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1368b6737651SAggelos Economopoulos 		return;
1369b6737651SAggelos Economopoulos 	}
1370b6737651SAggelos Economopoulos 
1371b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
13728892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13738892ea20SAggelos Economopoulos 
13747cc92483SSepherosa Ziehau 	/*
13757cc92483SSepherosa Ziehau 	 * Random information
13767cc92483SSepherosa Ziehau 	 */
13777cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13787cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
13798892ea20SAggelos Economopoulos 
13807cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
13817cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
13828892ea20SAggelos Economopoulos 
13837cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
13847cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
13858892ea20SAggelos Economopoulos 
13867cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
13877cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
138889d55360SSepherosa Ziehau 
13897cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
13907cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
13918892ea20SAggelos Economopoulos 
13927cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
13937cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
13948892ea20SAggelos Economopoulos 
13957cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
13967cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
13978892ea20SAggelos Economopoulos 
13987cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
13997cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
14008892ea20SAggelos Economopoulos 
14017cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14027cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14037cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14047cc92483SSepherosa Ziehau 
14057cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14067cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14077cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14087cc92483SSepherosa Ziehau 
14097cc92483SSepherosa Ziehau 	/*
14107cc92483SSepherosa Ziehau 	 * Performance related tunables
14117cc92483SSepherosa Ziehau 	 */
14127cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14137cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14147cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14157cc92483SSepherosa Ziehau 
14167cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14177cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14187cc92483SSepherosa Ziehau 	    "Transmit throttling");
14197cc92483SSepherosa Ziehau 
14207cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "flow_control_enabled",
14217cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_flow_control, "I",
14227cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14237cc92483SSepherosa Ziehau 
14247cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14257cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14267cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14277cc92483SSepherosa Ziehau 
14287cc92483SSepherosa Ziehau 	/*
14297cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14307cc92483SSepherosa Ziehau 	 * Need to swap it
14317cc92483SSepherosa Ziehau 	 */
14327cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14337cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14347cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14357cc92483SSepherosa Ziehau 
14367cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14377cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14387cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14397cc92483SSepherosa Ziehau 
14407cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14417cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14427cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14437cc92483SSepherosa Ziehau 
14447cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14457cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14467cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14477cc92483SSepherosa Ziehau 
14487cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14497cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14507cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14517cc92483SSepherosa Ziehau 
14527cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14537cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14547cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14557cc92483SSepherosa Ziehau 
14567cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14577cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14587cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14597cc92483SSepherosa Ziehau 
14607cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14617cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14627cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14637cc92483SSepherosa Ziehau 
14647cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14657cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14667cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14677cc92483SSepherosa Ziehau 
14687cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14697cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14707cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14717cc92483SSepherosa Ziehau 
14727cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14737cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14747cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14757cc92483SSepherosa Ziehau 
14767cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14777cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14787cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
14797cc92483SSepherosa Ziehau 
14807cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
14817cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
14827cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
14838892ea20SAggelos Economopoulos 
14848892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
14858892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
14867cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
14877cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1488798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1489798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1490798c3369SSepherosa Ziehau 		return;
1491798c3369SSepherosa Ziehau 	}
14928892ea20SAggelos Economopoulos 
14938892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
14948892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
14958892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
14968892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
14978892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1498b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
14997cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15007cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1501798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1502798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1503798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1504798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1505798c3369SSepherosa Ziehau 		}
15068892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15077cc92483SSepherosa Ziehau 
15087cc92483SSepherosa Ziehau 		/*
15097cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15107cc92483SSepherosa Ziehau 		 */
15117cc92483SSepherosa Ziehau 
15127cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15137cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_small.cnt, 0, "rx_small_cnt");
15147cc92483SSepherosa Ziehau 
15157cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15167cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_big.cnt, 0, "rx_small_cnt");
15178892ea20SAggelos Economopoulos 
15188892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
15198892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
15208892ea20SAggelos Economopoulos 		if (slice > 0)
15218892ea20SAggelos Economopoulos 			continue;
15228892ea20SAggelos Economopoulos #endif
15238892ea20SAggelos Economopoulos 
15247cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15257cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15267cc92483SSepherosa Ziehau 
15277cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15287cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15297cc92483SSepherosa Ziehau 
15307cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15317cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15327cc92483SSepherosa Ziehau 
15337cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15347cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15357cc92483SSepherosa Ziehau 
15367cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15377cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15387cc92483SSepherosa Ziehau 
15397cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15407cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15418892ea20SAggelos Economopoulos 	}
15428892ea20SAggelos Economopoulos }
15438892ea20SAggelos Economopoulos 
154489d55360SSepherosa Ziehau /*
154589d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
154689d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
154789d55360SSepherosa Ziehau  */
1548ddbf91b7SSepherosa Ziehau static __inline void
15498892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15508892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15518892ea20SAggelos Economopoulos {
15528892ea20SAggelos Economopoulos 	int idx, starting_slot;
15535ca32f31SSepherosa Ziehau 
15548892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15558892ea20SAggelos Economopoulos 	while (cnt > 1) {
15568892ea20SAggelos Economopoulos 		cnt--;
15578892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15585ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15598892ea20SAggelos Economopoulos 		wmb();
15608892ea20SAggelos Economopoulos 	}
15618892ea20SAggelos Economopoulos }
15628892ea20SAggelos Economopoulos 
15638892ea20SAggelos Economopoulos /*
156489d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15658892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15668892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15678892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15688892ea20SAggelos Economopoulos  */
1569ddbf91b7SSepherosa Ziehau static __inline void
157089d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15718892ea20SAggelos Economopoulos {
15728892ea20SAggelos Economopoulos 	int idx, i;
15738892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15748892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15758892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15768892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15778892ea20SAggelos Economopoulos 	uint8_t last_flags;
15788892ea20SAggelos Economopoulos 
15798892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15808892ea20SAggelos Economopoulos 
15818892ea20SAggelos Economopoulos 	last_flags = src->flags;
15828892ea20SAggelos Economopoulos 	src->flags = 0;
15838892ea20SAggelos Economopoulos 	wmb();
15848892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
15858892ea20SAggelos Economopoulos 	srcp = src;
15868892ea20SAggelos Economopoulos 
15878892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
15885ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
15898892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
15908892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
15918892ea20SAggelos Economopoulos 			srcp += 2;
15928892ea20SAggelos Economopoulos 			dstp += 2;
15938892ea20SAggelos Economopoulos 		}
15948892ea20SAggelos Economopoulos 	} else {
15955ca32f31SSepherosa Ziehau 		/*
15965ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
15975ca32f31SSepherosa Ziehau 		 * that it is submitted below
15985ca32f31SSepherosa Ziehau 		 */
15998892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16008892ea20SAggelos Economopoulos 		i = 0;
16018892ea20SAggelos Economopoulos 	}
16028892ea20SAggelos Economopoulos 	if (i < cnt) {
16035ca32f31SSepherosa Ziehau 		/* Submit the first request */
16048892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16058892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16068892ea20SAggelos Economopoulos 	}
16078892ea20SAggelos Economopoulos 
16085ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16098892ea20SAggelos Economopoulos 	src->flags = last_flags;
16108892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16118892ea20SAggelos Economopoulos 	src_ints+=3;
16128892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16138892ea20SAggelos Economopoulos 	dst_ints+=3;
16148892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16158892ea20SAggelos Economopoulos 	tx->req += cnt;
16168892ea20SAggelos Economopoulos 	wmb();
16178892ea20SAggelos Economopoulos }
16188892ea20SAggelos Economopoulos 
161989d55360SSepherosa Ziehau static int
162089d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
162189d55360SSepherosa Ziehau {
162289d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
162389d55360SSepherosa Ziehau 	struct mbuf *m;
162489d55360SSepherosa Ziehau 
162589d55360SSepherosa Ziehau 	m = *mp;
162689d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
162789d55360SSepherosa Ziehau 
162889d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
162989d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
163089d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
163189d55360SSepherosa Ziehau 
163289d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
163389d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
163489d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
163589d55360SSepherosa Ziehau 
163689d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
163789d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
163889d55360SSepherosa Ziehau 		if (m == NULL) {
163989d55360SSepherosa Ziehau 			*mp = NULL;
164089d55360SSepherosa Ziehau 			return ENOBUFS;
164189d55360SSepherosa Ziehau 		}
164289d55360SSepherosa Ziehau 		*mp = m;
164389d55360SSepherosa Ziehau 	}
164489d55360SSepherosa Ziehau 	return 0;
164589d55360SSepherosa Ziehau }
16468892ea20SAggelos Economopoulos 
1647ca8ca004SSepherosa Ziehau static int
16485ca32f31SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mbuf *m, int busdma_seg_cnt)
16498892ea20SAggelos Economopoulos {
16508892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16518892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16528892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16538892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16548892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16558892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16568892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
16578892ea20SAggelos Economopoulos 
16588892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16598892ea20SAggelos Economopoulos 
16605ca32f31SSepherosa Ziehau 	/*
16615ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16625ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16638892ea20SAggelos Economopoulos 	 */
166489d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
166589d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16668892ea20SAggelos Economopoulos 
16675ca32f31SSepherosa Ziehau 	/*
16685ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16695ca32f31SSepherosa Ziehau 	 */
167089d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16718892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16728892ea20SAggelos Economopoulos 
16735ca32f31SSepherosa Ziehau 	/*
16745ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16755ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
16765ca32f31SSepherosa Ziehau 	 */
16778892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16788892ea20SAggelos Economopoulos 
16798892ea20SAggelos Economopoulos 	req = tx->req_list;
16808892ea20SAggelos Economopoulos 	seg = tx->seg_list;
16818892ea20SAggelos Economopoulos 	cnt = 0;
16828892ea20SAggelos Economopoulos 	rdma_count = 0;
16835ca32f31SSepherosa Ziehau 
16845ca32f31SSepherosa Ziehau 	/*
16855ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
16865ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
16875ca32f31SSepherosa Ziehau 	 * this is equal to "count".
16888892ea20SAggelos Economopoulos 	 *
16895ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
16905ca32f31SSepherosa Ziehau 	 * segment cut.
16918892ea20SAggelos Economopoulos 	 *
16925ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
16935ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
16945ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
16955ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
16965ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
16975ca32f31SSepherosa Ziehau 	 * RDMA.
16985ca32f31SSepherosa Ziehau 	 *
16995ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
17005ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17015ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17028892ea20SAggelos Economopoulos 	 */
17038892ea20SAggelos Economopoulos 
17048892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17055ca32f31SSepherosa Ziehau 		/*
17065ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17075ca32f31SSepherosa Ziehau 		 */
17088892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17098892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17108892ea20SAggelos Economopoulos 		len = seg->ds_len;
17118892ea20SAggelos Economopoulos 
17128892ea20SAggelos Economopoulos 		while (len) {
17138892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17148892ea20SAggelos Economopoulos 			seglen = len;
17158892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17168892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17178892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17185ca32f31SSepherosa Ziehau 				/* Payload */
17198892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17208892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17218892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17228892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17235ca32f31SSepherosa Ziehau 				flags_next |=
17245ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17258892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17268892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17278892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17285ca32f31SSepherosa Ziehau 				/* Header ends */
17298892ea20SAggelos Economopoulos 				rdma_count = -1;
17308892ea20SAggelos Economopoulos 				cum_len_next = 0;
17318892ea20SAggelos Economopoulos 				seglen = -cum_len;
17328892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17338892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17348892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17358892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17368892ea20SAggelos Economopoulos 			}
17378892ea20SAggelos Economopoulos 
17388892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17398892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17408892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17418892ea20SAggelos Economopoulos 			req->pad = 0;
17428892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17438892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17448892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17455ca32f31SSepherosa Ziehau 			req->flags =
17465ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17478892ea20SAggelos Economopoulos 			low += seglen;
17488892ea20SAggelos Economopoulos 			len -= seglen;
17498892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17508892ea20SAggelos Economopoulos 			flags = flags_next;
17518892ea20SAggelos Economopoulos 			req++;
17528892ea20SAggelos Economopoulos 			cnt++;
17538892ea20SAggelos Economopoulos 			rdma_count++;
17548892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17558892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17568892ea20SAggelos Economopoulos 			else
17578892ea20SAggelos Economopoulos 				cksum_offset = 0;
17588892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17598892ea20SAggelos Economopoulos 				goto drop;
17608892ea20SAggelos Economopoulos 		}
17618892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17628892ea20SAggelos Economopoulos 		seg++;
17638892ea20SAggelos Economopoulos 	}
17648892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17658892ea20SAggelos Economopoulos 
17668892ea20SAggelos Economopoulos 	do {
17678892ea20SAggelos Economopoulos 		req--;
17688892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17698892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17708892ea20SAggelos Economopoulos 
17718892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
17728892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17738892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
17748892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
17758892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
17768892ea20SAggelos Economopoulos 		*tx->send_go = 1;
17778892ea20SAggelos Economopoulos 		tx->queue_active = 1;
17788892ea20SAggelos Economopoulos 		tx->activate++;
17798892ea20SAggelos Economopoulos 		wmb();
17808892ea20SAggelos Economopoulos 	}
17818892ea20SAggelos Economopoulos #endif
1782ca8ca004SSepherosa Ziehau 	return 0;
17838892ea20SAggelos Economopoulos 
17848892ea20SAggelos Economopoulos drop:
17858892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
17868892ea20SAggelos Economopoulos 	m_freem(m);
1787ca8ca004SSepherosa Ziehau 	return ENOBUFS;
17888892ea20SAggelos Economopoulos }
17898892ea20SAggelos Economopoulos 
1790ca8ca004SSepherosa Ziehau static int
17918892ea20SAggelos Economopoulos mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
17928892ea20SAggelos Economopoulos {
17938892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
17948892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
17958892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
17968892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
179789d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
17988892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
17998892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
18008892ea20SAggelos Economopoulos 
18018892ea20SAggelos Economopoulos 	sc = ss->sc;
18028892ea20SAggelos Economopoulos 	tx = &ss->tx;
18038892ea20SAggelos Economopoulos 
180489d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1805ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1806ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1807ca8ca004SSepherosa Ziehau 			return err;
18088892ea20SAggelos Economopoulos 	}
180989d55360SSepherosa Ziehau 
18105ca32f31SSepherosa Ziehau 	/*
18115ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18125ca32f31SSepherosa Ziehau 	 */
181389d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
181489d55360SSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, tx->info[idx].map, &m,
181589d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
181689d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
181789d55360SSepherosa Ziehau 		goto drop;
181889d55360SSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, tx->info[idx].map, BUS_DMASYNC_PREWRITE);
181989d55360SSepherosa Ziehau 	tx->info[idx].m = m;
182089d55360SSepherosa Ziehau 
18215ca32f31SSepherosa Ziehau 	/*
18225ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18235ca32f31SSepherosa Ziehau 	 */
1824ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
1825ca8ca004SSepherosa Ziehau 		return mxge_encap_tso(tx, m, cnt);
18268892ea20SAggelos Economopoulos 
18278892ea20SAggelos Economopoulos 	req = tx->req_list;
18288892ea20SAggelos Economopoulos 	cksum_offset = 0;
18298892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18308892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18318892ea20SAggelos Economopoulos 
18325ca32f31SSepherosa Ziehau 	/*
18335ca32f31SSepherosa Ziehau 	 * Checksum offloading
18345ca32f31SSepherosa Ziehau 	 */
183589d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
183689d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18378892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18388892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18398892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18408892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18418892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18428892ea20SAggelos Economopoulos 	} else {
18438892ea20SAggelos Economopoulos 		odd_flag = 0;
18448892ea20SAggelos Economopoulos 	}
18458892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18468892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18478892ea20SAggelos Economopoulos 
18485ca32f31SSepherosa Ziehau 	/*
18495ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18505ca32f31SSepherosa Ziehau 	 */
18518892ea20SAggelos Economopoulos 	cum_len = 0;
18528892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18538892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18548892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18555ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18565ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18578892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18588892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18598892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18608892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18618892ea20SAggelos Economopoulos 		else
18628892ea20SAggelos Economopoulos 			cksum_offset = 0;
18638892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18648892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18658892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18668892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18678892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18688892ea20SAggelos Economopoulos 		seg++;
18698892ea20SAggelos Economopoulos 		req++;
18708892ea20SAggelos Economopoulos 		req->flags = 0;
18718892ea20SAggelos Economopoulos 	}
18728892ea20SAggelos Economopoulos 	req--;
18735ca32f31SSepherosa Ziehau 
18745ca32f31SSepherosa Ziehau 	/*
18755ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
18765ca32f31SSepherosa Ziehau 	 */
18778892ea20SAggelos Economopoulos 	if (cum_len < 60) {
18788892ea20SAggelos Economopoulos 		req++;
18798892ea20SAggelos Economopoulos 		req->addr_low =
18807cc92483SSepherosa Ziehau 		    htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
18818892ea20SAggelos Economopoulos 		req->addr_high =
18827cc92483SSepherosa Ziehau 		    htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
18838892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
18848892ea20SAggelos Economopoulos 		req->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 		cnt++;
18908892ea20SAggelos Economopoulos 	}
18918892ea20SAggelos Economopoulos 
18928892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
18938892ea20SAggelos Economopoulos #if 0
18948892ea20SAggelos Economopoulos 	/* print what the firmware will see */
18958892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18966c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
18978892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
18988892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
18998892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19008892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19018892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19028892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19038892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19048892ea20SAggelos Economopoulos 	}
19056c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19068892ea20SAggelos Economopoulos #endif
19078892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19088892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19098892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19108892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19118892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19128892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19138892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19148892ea20SAggelos Economopoulos 		tx->activate++;
19158892ea20SAggelos Economopoulos 		wmb();
19168892ea20SAggelos Economopoulos 	}
19178892ea20SAggelos Economopoulos #endif
1918ca8ca004SSepherosa Ziehau 	return 0;
19198892ea20SAggelos Economopoulos 
19208892ea20SAggelos Economopoulos drop:
19218892ea20SAggelos Economopoulos 	m_freem(m);
1922ca8ca004SSepherosa Ziehau 	return err;
19238892ea20SAggelos Economopoulos }
19248892ea20SAggelos Economopoulos 
19258892ea20SAggelos Economopoulos static void
1926f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19278892ea20SAggelos Economopoulos {
19288892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
19298892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
1930795c96bbSSepherosa Ziehau 	mxge_tx_ring_t *tx;
1931ca8ca004SSepherosa Ziehau 	int encap = 0;
19328892ea20SAggelos Economopoulos 
1933f0a26983SSepherosa Ziehau 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
1934cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
1935795c96bbSSepherosa Ziehau 
1936795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1937795c96bbSSepherosa Ziehau 		return;
1938795c96bbSSepherosa Ziehau 
1939795c96bbSSepherosa Ziehau 	/* XXX Only use the first slice for now */
19408892ea20SAggelos Economopoulos 	ss = &sc->ss[0];
1941795c96bbSSepherosa Ziehau 	tx = &ss->tx;
1942795c96bbSSepherosa Ziehau 
1943795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1944795c96bbSSepherosa Ziehau 		struct mbuf *m;
1945ca8ca004SSepherosa Ziehau 		int error;
1946795c96bbSSepherosa Ziehau 
1947795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1948795c96bbSSepherosa Ziehau 		if (m == NULL)
1949ca8ca004SSepherosa Ziehau 			goto done;
1950795c96bbSSepherosa Ziehau 
1951795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
1952ca8ca004SSepherosa Ziehau 		error = mxge_encap(ss, m);
1953ca8ca004SSepherosa Ziehau 		if (!error)
1954ca8ca004SSepherosa Ziehau 			encap = 1;
1955*fed54363SSepherosa Ziehau 		else
1956*fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1957795c96bbSSepherosa Ziehau 	}
1958795c96bbSSepherosa Ziehau 
1959795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1960795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1961ca8ca004SSepherosa Ziehau done:
1962ca8ca004SSepherosa Ziehau 	if (encap)
1963ca8ca004SSepherosa Ziehau 		ifp->if_timer = 5;
1964ca8ca004SSepherosa Ziehau }
1965ca8ca004SSepherosa Ziehau 
1966ca8ca004SSepherosa Ziehau static void
1967ca8ca004SSepherosa Ziehau mxge_watchdog(struct ifnet *ifp)
1968ca8ca004SSepherosa Ziehau {
1969ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1970ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1971ca8ca004SSepherosa Ziehau 	mxge_tx_ring_t *tx = &sc->ss[0].tx;
1972ca8ca004SSepherosa Ziehau 
1973ca8ca004SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
1974ca8ca004SSepherosa Ziehau 
1975ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
1976ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
1977ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
1978ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
1979ca8ca004SSepherosa Ziehau 		return;
1980ca8ca004SSepherosa Ziehau 	} else {
1981ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
1982ca8ca004SSepherosa Ziehau 		    "check link partner\n");
1983ca8ca004SSepherosa Ziehau 	}
1984ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
19858892ea20SAggelos Economopoulos }
19868892ea20SAggelos Economopoulos 
19878892ea20SAggelos Economopoulos /*
19882f47b54fSSepherosa Ziehau  * 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 
23528892ea20SAggelos Economopoulos 	tx = &ss->tx;
23538892ea20SAggelos Economopoulos 	ifp = ss->sc->ifp;
2354cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
235566e7a0e8SSepherosa Ziehau 
23568892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
235766e7a0e8SSepherosa Ziehau 		struct mbuf *m;
235866e7a0e8SSepherosa Ziehau 		int idx;
235966e7a0e8SSepherosa Ziehau 
23608892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23618892ea20SAggelos Economopoulos 		tx->done++;
236266e7a0e8SSepherosa Ziehau 
23638892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
236466e7a0e8SSepherosa Ziehau 		/*
236566e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
236666e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
236766e7a0e8SSepherosa Ziehau 		 */
23688892ea20SAggelos Economopoulos 		if (m != NULL) {
23698892ea20SAggelos Economopoulos 			ss->opackets++;
23708892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
237166e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].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 
238066e7a0e8SSepherosa Ziehau 	/*
238166e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
238266e7a0e8SSepherosa Ziehau 	 * its OK to send packets
238366e7a0e8SSepherosa Ziehau 	 */
2384ca8ca004SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 4) {
23859ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
2386ca8ca004SSepherosa Ziehau 		if (tx->req == tx->done)
2387ca8ca004SSepherosa Ziehau 			ifp->if_timer = 0;
2388ca8ca004SSepherosa Ziehau 	}
238989d55360SSepherosa Ziehau 
239089d55360SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
239189d55360SSepherosa Ziehau 		if_devstart(ifp);
239289d55360SSepherosa Ziehau 
23938892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
23948892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
23958892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
23968892ea20SAggelos Economopoulos 		 * are no more transmits pending */
23978892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
23988892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
23998892ea20SAggelos Economopoulos 			tx->queue_active = 0;
24008892ea20SAggelos Economopoulos 			tx->deactivate++;
24018892ea20SAggelos Economopoulos 			wmb();
24028892ea20SAggelos Economopoulos 		}
24038892ea20SAggelos Economopoulos 	}
24048892ea20SAggelos Economopoulos #endif
24058892ea20SAggelos Economopoulos }
24068892ea20SAggelos Economopoulos 
240789d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24088892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24098892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24108892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
24118892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
24128892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
24138892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
24148892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
24158892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
24168892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
24178892ea20SAggelos Economopoulos };
241889d55360SSepherosa Ziehau 
241989d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
242089d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
24218892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
24228892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24238892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
242489d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
242589d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24268892ea20SAggelos Economopoulos };
24278892ea20SAggelos Economopoulos 
24288892ea20SAggelos Economopoulos static void
242989d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24308892ea20SAggelos Economopoulos {
24317cc92483SSepherosa Ziehau 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 0, NULL);
243289d55360SSepherosa Ziehau 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
243389d55360SSepherosa Ziehau 	sc->current_media = media_type;
243489d55360SSepherosa Ziehau 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
24358892ea20SAggelos Economopoulos }
24368892ea20SAggelos Economopoulos 
24378892ea20SAggelos Economopoulos static void
243889d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24398892ea20SAggelos Economopoulos {
2440c7431c78SSepherosa Ziehau 	const char *ptr;
244189d55360SSepherosa Ziehau 	int i;
24428892ea20SAggelos Economopoulos 
244389d55360SSepherosa Ziehau 	ifmedia_removeall(&sc->media);
244489d55360SSepherosa Ziehau 	mxge_media_set(sc, IFM_AUTO);
24458892ea20SAggelos Economopoulos 
24468892ea20SAggelos Economopoulos 	/*
24472f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24488892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24498892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24508892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24518892ea20SAggelos Economopoulos 	 */
24528892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24538892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2454af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
245589d55360SSepherosa Ziehau 		return;
24568892ea20SAggelos Economopoulos 	}
24578892ea20SAggelos Economopoulos 
24588892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
245989d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24608892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2461af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24628892ea20SAggelos Economopoulos 			return;
24638892ea20SAggelos Economopoulos 		}
24648892ea20SAggelos Economopoulos 	}
246589d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24668892ea20SAggelos Economopoulos 		/* -C is CX4 */
246789d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
246889d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
246989d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24708892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
247189d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2472af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24732f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
247489d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
247589d55360SSepherosa Ziehau 		/* -R is XFP */
247689d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
247789d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
247889d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
247989d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
248089d55360SSepherosa Ziehau 	} else {
2481af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
248289d55360SSepherosa Ziehau 	}
24838892ea20SAggelos Economopoulos }
24848892ea20SAggelos Economopoulos 
248589d55360SSepherosa Ziehau /*
248689d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
248789d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
248889d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
248989d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
249089d55360SSepherosa Ziehau  * than in the interrupt handler itself.
249189d55360SSepherosa Ziehau  */
249289d55360SSepherosa Ziehau static void
249389d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
249489d55360SSepherosa Ziehau {
249589d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
24967cc92483SSepherosa Ziehau 	const char *cage_type;
249789d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
249889d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
249989d55360SSepherosa Ziehau 	uint32_t byte;
250089d55360SSepherosa Ziehau 
250189d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
250289d55360SSepherosa Ziehau 
250389d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
25048892ea20SAggelos Economopoulos 		/* -R is XFP */
25058892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
25067cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_xfp_media_types) /
250789d55360SSepherosa Ziehau 		    sizeof(mxge_xfp_media_types[0]);
25088892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25098892ea20SAggelos Economopoulos 		cage_type = "XFP";
251089d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25118892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25128892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
25137cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_sfp_media_types) /
251489d55360SSepherosa Ziehau 		    sizeof(mxge_sfp_media_types[0]);
25158892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25168892ea20SAggelos Economopoulos 		byte = 3;
251789d55360SSepherosa Ziehau 	} else {
251889d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25198892ea20SAggelos Economopoulos 		return;
25208892ea20SAggelos Economopoulos 	}
25218892ea20SAggelos Economopoulos 
25228892ea20SAggelos Economopoulos 	/*
25238892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25248892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25258892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25268892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25278892ea20SAggelos Economopoulos 	 * a millisecond
25288892ea20SAggelos Economopoulos 	 */
25298892ea20SAggelos Economopoulos 
25308892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25318892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25328892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
25337cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2534af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read XFP\n");
25357cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2536af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
25377cc92483SSepherosa Ziehau 	if (err != MXGEFW_CMD_OK)
25388892ea20SAggelos Economopoulos 		return;
25398892ea20SAggelos Economopoulos 
25407cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25418892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25428892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25437cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25448892ea20SAggelos Economopoulos 		DELAY(1000);
25458892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25468892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25478892ea20SAggelos Economopoulos 	}
25488892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2549af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25508892ea20SAggelos Economopoulos 		    cage_type, err, ms);
25518892ea20SAggelos Economopoulos 		return;
25528892ea20SAggelos Economopoulos 	}
25538892ea20SAggelos Economopoulos 
25548892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25557cc92483SSepherosa Ziehau 		if (bootverbose) {
2556af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25578892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25587cc92483SSepherosa Ziehau 		}
255989d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
256089d55360SSepherosa Ziehau 			mxge_media_init(sc);
256189d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
256289d55360SSepherosa Ziehau 		}
25638892ea20SAggelos Economopoulos 		return;
25648892ea20SAggelos Economopoulos 	}
25658892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25668892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25677cc92483SSepherosa Ziehau 			if (bootverbose) {
2568af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25698892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25707cc92483SSepherosa Ziehau 			}
25718892ea20SAggelos Economopoulos 
257289d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
257389d55360SSepherosa Ziehau 				mxge_media_init(sc);
257489d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
257589d55360SSepherosa Ziehau 			}
25768892ea20SAggelos Economopoulos 			return;
25778892ea20SAggelos Economopoulos 		}
25788892ea20SAggelos Economopoulos 	}
25797cc92483SSepherosa Ziehau 	if (bootverbose) {
2580af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
25817cc92483SSepherosa Ziehau 		    cmd.data0);
25827cc92483SSepherosa Ziehau 	}
25838892ea20SAggelos Economopoulos }
25848892ea20SAggelos Economopoulos 
25858892ea20SAggelos Economopoulos static void
25868892ea20SAggelos Economopoulos mxge_intr(void *arg)
25878892ea20SAggelos Economopoulos {
25888892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss = arg;
25898892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
25908892ea20SAggelos Economopoulos 	mcp_irq_data_t *stats = ss->fw_stats;
25918892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx = &ss->tx;
25928892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
25938892ea20SAggelos Economopoulos 	uint32_t send_done_count;
25948892ea20SAggelos Economopoulos 	uint8_t valid;
25958892ea20SAggelos Economopoulos 
25968892ea20SAggelos Economopoulos 
25978892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
25988892ea20SAggelos Economopoulos 	/* an interrupt on a non-zero slice is implicitly valid
25998892ea20SAggelos Economopoulos 	   since MSI-X irqs are not shared */
26008892ea20SAggelos Economopoulos 	if (ss != sc->ss) {
26018892ea20SAggelos Economopoulos 		mxge_clean_rx_done(ss);
26028892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
26038892ea20SAggelos Economopoulos 		return;
26048892ea20SAggelos Economopoulos 	}
26058892ea20SAggelos Economopoulos #endif
26068892ea20SAggelos Economopoulos 
26078892ea20SAggelos Economopoulos 	/* make sure the DMA has finished */
26088892ea20SAggelos Economopoulos 	if (!stats->valid) {
26098892ea20SAggelos Economopoulos 		return;
26108892ea20SAggelos Economopoulos 	}
26118892ea20SAggelos Economopoulos 	valid = stats->valid;
26128892ea20SAggelos Economopoulos 
261389d55360SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_LEGACY) {
26148892ea20SAggelos Economopoulos 		/* lower legacy IRQ  */
26158892ea20SAggelos Economopoulos 		*sc->irq_deassert = 0;
26168892ea20SAggelos Economopoulos 		if (!mxge_deassert_wait)
26178892ea20SAggelos Economopoulos 			/* don't wait for conf. that irq is low */
26188892ea20SAggelos Economopoulos 			stats->valid = 0;
26198892ea20SAggelos Economopoulos 	} else {
26208892ea20SAggelos Economopoulos 		stats->valid = 0;
26218892ea20SAggelos Economopoulos 	}
26228892ea20SAggelos Economopoulos 
26238892ea20SAggelos Economopoulos 	/* loop while waiting for legacy irq deassertion */
26248892ea20SAggelos Economopoulos 	do {
26258892ea20SAggelos Economopoulos 		/* check for transmit completes and receives */
26268892ea20SAggelos Economopoulos 		send_done_count = be32toh(stats->send_done_count);
26278892ea20SAggelos Economopoulos 		while ((send_done_count != tx->pkt_done) ||
26288892ea20SAggelos Economopoulos 		       (rx_done->entry[rx_done->idx].length != 0)) {
26298892ea20SAggelos Economopoulos 			if (send_done_count != tx->pkt_done)
26308892ea20SAggelos Economopoulos 				mxge_tx_done(ss, (int)send_done_count);
26318892ea20SAggelos Economopoulos 			mxge_clean_rx_done(ss);
26328892ea20SAggelos Economopoulos 			send_done_count = be32toh(stats->send_done_count);
26338892ea20SAggelos Economopoulos 		}
263489d55360SSepherosa Ziehau 		if (sc->irq_type == PCI_INTR_TYPE_LEGACY && mxge_deassert_wait)
26358892ea20SAggelos Economopoulos 			wmb();
26368892ea20SAggelos Economopoulos 	} while (*((volatile uint8_t *) &stats->valid));
26378892ea20SAggelos Economopoulos 
26388892ea20SAggelos Economopoulos 	/* fw link & error stats meaningful only on the first slice */
26398892ea20SAggelos Economopoulos 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
26408892ea20SAggelos Economopoulos 		if (sc->link_state != stats->link_up) {
26418892ea20SAggelos Economopoulos 			sc->link_state = stats->link_up;
26428892ea20SAggelos Economopoulos 			if (sc->link_state) {
264373a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_UP;
264473a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26457cc92483SSepherosa Ziehau 				if (bootverbose)
26468892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link up\n");
26478892ea20SAggelos Economopoulos 			} else {
264873a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_DOWN;
264973a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26507cc92483SSepherosa Ziehau 				if (bootverbose)
26518892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link down\n");
26528892ea20SAggelos Economopoulos 			}
26538892ea20SAggelos Economopoulos 			sc->need_media_probe = 1;
26548892ea20SAggelos Economopoulos 		}
26558892ea20SAggelos Economopoulos 		if (sc->rdma_tags_available !=
26568892ea20SAggelos Economopoulos 		    be32toh(stats->rdma_tags_available)) {
26578892ea20SAggelos Economopoulos 			sc->rdma_tags_available =
26588892ea20SAggelos Economopoulos 				be32toh(stats->rdma_tags_available);
26598892ea20SAggelos Economopoulos 			device_printf(sc->dev, "RDMA timed out! %d tags "
26608892ea20SAggelos Economopoulos 				      "left\n", sc->rdma_tags_available);
26618892ea20SAggelos Economopoulos 		}
26628892ea20SAggelos Economopoulos 
26638892ea20SAggelos Economopoulos 		if (stats->link_down) {
26648892ea20SAggelos Economopoulos 			sc->down_cnt += stats->link_down;
26658892ea20SAggelos Economopoulos 			sc->link_state = 0;
2666f0115d64SAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
2667f0115d64SAggelos Economopoulos 			if_link_state_change(sc->ifp);
26688892ea20SAggelos Economopoulos 		}
26698892ea20SAggelos Economopoulos 	}
26708892ea20SAggelos Economopoulos 
26718892ea20SAggelos Economopoulos 	/* check to see if we have rx token to pass back */
26728892ea20SAggelos Economopoulos 	if (valid & 0x1)
26738892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
26748892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
26758892ea20SAggelos Economopoulos }
26768892ea20SAggelos Economopoulos 
26778892ea20SAggelos Economopoulos static void
26788892ea20SAggelos Economopoulos mxge_init(void *arg)
26798892ea20SAggelos Economopoulos {
268089d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
268189d55360SSepherosa Ziehau 
268289d55360SSepherosa Ziehau 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
268389d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
268489d55360SSepherosa Ziehau 		mxge_open(sc);
26858892ea20SAggelos Economopoulos }
26868892ea20SAggelos Economopoulos 
26878892ea20SAggelos Economopoulos static void
26888892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
26898892ea20SAggelos Economopoulos {
26908892ea20SAggelos Economopoulos 	int i;
26918892ea20SAggelos Economopoulos 
26928892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
26938892ea20SAggelos Economopoulos 		if (ss->rx_big.info[i].m == NULL)
26948892ea20SAggelos Economopoulos 			continue;
26954e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->rx_big.dmat, ss->rx_big.info[i].map);
26968892ea20SAggelos Economopoulos 		m_freem(ss->rx_big.info[i].m);
26978892ea20SAggelos Economopoulos 		ss->rx_big.info[i].m = NULL;
26988892ea20SAggelos Economopoulos 	}
26998892ea20SAggelos Economopoulos 
27008892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
27018892ea20SAggelos Economopoulos 		if (ss->rx_small.info[i].m == NULL)
27028892ea20SAggelos Economopoulos 			continue;
27034e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->rx_small.dmat, ss->rx_small.info[i].map);
27048892ea20SAggelos Economopoulos 		m_freem(ss->rx_small.info[i].m);
27058892ea20SAggelos Economopoulos 		ss->rx_small.info[i].m = NULL;
27068892ea20SAggelos Economopoulos 	}
27078892ea20SAggelos Economopoulos 
27084e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
27098892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
27108892ea20SAggelos Economopoulos 		return;
27118892ea20SAggelos Economopoulos 
27128892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
27138892ea20SAggelos Economopoulos 		ss->tx.info[i].flag = 0;
27148892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
27158892ea20SAggelos Economopoulos 			continue;
27164e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
27178892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
27188892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
27198892ea20SAggelos Economopoulos 	}
27208892ea20SAggelos Economopoulos }
27218892ea20SAggelos Economopoulos 
27228892ea20SAggelos Economopoulos static void
27238892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
27248892ea20SAggelos Economopoulos {
27258892ea20SAggelos Economopoulos 	int slice;
27268892ea20SAggelos Economopoulos 
27278892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
27288892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
27298892ea20SAggelos Economopoulos }
27308892ea20SAggelos Economopoulos 
27318892ea20SAggelos Economopoulos static void
27328892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
27338892ea20SAggelos Economopoulos {
27348892ea20SAggelos Economopoulos 	int i;
27358892ea20SAggelos Economopoulos 
2736798c3369SSepherosa Ziehau 	if (ss->rx_done.entry != NULL) {
27378892ea20SAggelos Economopoulos 		mxge_dma_free(&ss->rx_done.dma);
27388892ea20SAggelos Economopoulos 		ss->rx_done.entry = NULL;
2739798c3369SSepherosa Ziehau 	}
27408892ea20SAggelos Economopoulos 
274111868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
274211868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
274311868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2744798c3369SSepherosa Ziehau 	}
27458892ea20SAggelos Economopoulos 
2746798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2747d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
27488892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2749798c3369SSepherosa Ziehau 	}
27508892ea20SAggelos Economopoulos 
2751798c3369SSepherosa Ziehau 	if (ss->rx_small.shadow != NULL) {
2752d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.shadow, M_DEVBUF);
27538892ea20SAggelos Economopoulos 		ss->rx_small.shadow = NULL;
2754798c3369SSepherosa Ziehau 	}
27558892ea20SAggelos Economopoulos 
2756798c3369SSepherosa Ziehau 	if (ss->rx_big.shadow != NULL) {
2757d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.shadow, M_DEVBUF);
27588892ea20SAggelos Economopoulos 		ss->rx_big.shadow = NULL;
2759798c3369SSepherosa Ziehau 	}
27608892ea20SAggelos Economopoulos 
27618892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
27628892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
27638892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
27648892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
27658892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
27668892ea20SAggelos Economopoulos 			}
27678892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
27688892ea20SAggelos Economopoulos 		}
2769d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
27708892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2771798c3369SSepherosa Ziehau 	}
27728892ea20SAggelos Economopoulos 
27738892ea20SAggelos Economopoulos 	if (ss->rx_small.info != NULL) {
27748892ea20SAggelos Economopoulos 		if (ss->rx_small.dmat != NULL) {
27758892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_small.mask; i++) {
27768892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_small.dmat,
27778892ea20SAggelos Economopoulos 				    ss->rx_small.info[i].map);
27788892ea20SAggelos Economopoulos 			}
27798892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_small.dmat,
27808892ea20SAggelos Economopoulos 			    ss->rx_small.extra_map);
27818892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_small.dmat);
27828892ea20SAggelos Economopoulos 		}
2783d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.info, M_DEVBUF);
27848892ea20SAggelos Economopoulos 		ss->rx_small.info = NULL;
2785798c3369SSepherosa Ziehau 	}
27868892ea20SAggelos Economopoulos 
27878892ea20SAggelos Economopoulos 	if (ss->rx_big.info != NULL) {
27888892ea20SAggelos Economopoulos 		if (ss->rx_big.dmat != NULL) {
27898892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_big.mask; i++) {
27908892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_big.dmat,
27918892ea20SAggelos Economopoulos 				    ss->rx_big.info[i].map);
27928892ea20SAggelos Economopoulos 			}
27938892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_big.dmat,
27948892ea20SAggelos Economopoulos 			    ss->rx_big.extra_map);
27958892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_big.dmat);
27968892ea20SAggelos Economopoulos 		}
2797d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.info, M_DEVBUF);
27988892ea20SAggelos Economopoulos 		ss->rx_big.info = NULL;
27998892ea20SAggelos Economopoulos 	}
2800798c3369SSepherosa Ziehau }
28018892ea20SAggelos Economopoulos 
28028892ea20SAggelos Economopoulos static void
28038892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
28048892ea20SAggelos Economopoulos {
28058892ea20SAggelos Economopoulos 	int slice;
28068892ea20SAggelos Economopoulos 
2807798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2808798c3369SSepherosa Ziehau 		return;
2809798c3369SSepherosa Ziehau 
28108892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
28118892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
28128892ea20SAggelos Economopoulos }
28138892ea20SAggelos Economopoulos 
28148892ea20SAggelos Economopoulos static int
28158892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28168892ea20SAggelos Economopoulos     int tx_ring_entries)
28178892ea20SAggelos Economopoulos {
28188892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
28198892ea20SAggelos Economopoulos 	size_t bytes;
28208892ea20SAggelos Economopoulos 	int err, i;
28218892ea20SAggelos Economopoulos 
28227cc92483SSepherosa Ziehau 	/*
28237cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
28247cc92483SSepherosa Ziehau 	 */
28258892ea20SAggelos Economopoulos 
28268892ea20SAggelos Economopoulos 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
28278892ea20SAggelos Economopoulos 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
28288892ea20SAggelos Economopoulos 
28297cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
28308892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow);
2831d777b84fSAggelos Economopoulos 	ss->rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28328892ea20SAggelos Economopoulos 
28338892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow);
2834d777b84fSAggelos Economopoulos 	ss->rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28358892ea20SAggelos Economopoulos 
28367cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
28378892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.info);
2838d777b84fSAggelos Economopoulos 	ss->rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28398892ea20SAggelos Economopoulos 
28408892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.info);
2841d777b84fSAggelos Economopoulos 	ss->rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28428892ea20SAggelos Economopoulos 
28437cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
28448892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28458892ea20SAggelos Economopoulos 				 1,			/* alignment */
28468892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28478892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28488892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28498892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
28508892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
28518892ea20SAggelos Economopoulos 				 1,			/* num segs */
28528892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
28537cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
28547cc92483SSepherosa Ziehau 				 			/* flags */
28558892ea20SAggelos Economopoulos 				 &ss->rx_small.dmat);	/* tag */
28568892ea20SAggelos Economopoulos 	if (err != 0) {
28578892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
28588892ea20SAggelos Economopoulos 		    err);
28593598cc14SSascha Wildner 		return err;
28608892ea20SAggelos Economopoulos 	}
28618892ea20SAggelos Economopoulos 
2862798c3369SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2863798c3369SSepherosa Ziehau 	    &ss->rx_small.extra_map);
2864798c3369SSepherosa Ziehau 	if (err != 0) {
2865798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
2866798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_small.dmat);
2867798c3369SSepherosa Ziehau 		ss->rx_small.dmat = NULL;
2868798c3369SSepherosa Ziehau 		return err;
2869798c3369SSepherosa Ziehau 	}
2870798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_small.mask; i++) {
2871798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2872798c3369SSepherosa Ziehau 		    &ss->rx_small.info[i].map);
2873798c3369SSepherosa Ziehau 		if (err != 0) {
2874798c3369SSepherosa Ziehau 			int j;
2875798c3369SSepherosa Ziehau 
2876798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
2877798c3369SSepherosa Ziehau 
2878798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2879798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_small.dmat,
2880798c3369SSepherosa Ziehau 				    ss->rx_small.info[j].map);
2881798c3369SSepherosa Ziehau 			}
2882798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_small.dmat,
2883798c3369SSepherosa Ziehau 			    ss->rx_small.extra_map);
2884798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_small.dmat);
2885798c3369SSepherosa Ziehau 			ss->rx_small.dmat = NULL;
2886798c3369SSepherosa Ziehau 			return err;
2887798c3369SSepherosa Ziehau 		}
2888798c3369SSepherosa Ziehau 	}
2889798c3369SSepherosa Ziehau 
28908892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28918892ea20SAggelos Economopoulos 				 1,			/* alignment */
28928892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28938892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28948892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28958892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2896b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
2897b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
28988892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
28997cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
29007cc92483SSepherosa Ziehau 				 			/* flags */
29018892ea20SAggelos Economopoulos 				 &ss->rx_big.dmat);	/* tag */
29028892ea20SAggelos Economopoulos 	if (err != 0) {
29038892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
29048892ea20SAggelos Economopoulos 		    err);
29053598cc14SSascha Wildner 		return err;
29068892ea20SAggelos Economopoulos 	}
29077cc92483SSepherosa Ziehau 
29087cc92483SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
29098892ea20SAggelos Economopoulos 	    &ss->rx_big.extra_map);
29108892ea20SAggelos Economopoulos 	if (err != 0) {
29117cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
2912798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_big.dmat);
2913798c3369SSepherosa Ziehau 		ss->rx_big.dmat = NULL;
29143598cc14SSascha Wildner 		return err;
29158892ea20SAggelos Economopoulos 	}
2916798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
2917798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
2918798c3369SSepherosa Ziehau 		    &ss->rx_big.info[i].map);
2919798c3369SSepherosa Ziehau 		if (err != 0) {
2920798c3369SSepherosa Ziehau 			int j;
2921798c3369SSepherosa Ziehau 
2922798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
2923798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2924798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_big.dmat,
2925798c3369SSepherosa Ziehau 				    ss->rx_big.info[j].map);
2926798c3369SSepherosa Ziehau 			}
2927798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_big.dmat,
2928798c3369SSepherosa Ziehau 			    ss->rx_big.extra_map);
2929798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_big.dmat);
2930798c3369SSepherosa Ziehau 			ss->rx_big.dmat = NULL;
2931798c3369SSepherosa Ziehau 			return err;
2932798c3369SSepherosa Ziehau 		}
2933798c3369SSepherosa Ziehau 	}
29348892ea20SAggelos Economopoulos 
29357cc92483SSepherosa Ziehau 	/*
29367cc92483SSepherosa Ziehau 	 * Now allocate TX resources
29377cc92483SSepherosa Ziehau 	 */
29388892ea20SAggelos Economopoulos 
29398892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
29408892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
29418892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
29428892ea20SAggelos Economopoulos 		return 0;
29438892ea20SAggelos Economopoulos #endif
29448892ea20SAggelos Economopoulos 
29458892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
29468892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
29478892ea20SAggelos Economopoulos 
294811868a93SSepherosa Ziehau 	/* Allocate the tx request copy block; MUST be 8 bytes aligned */
294911868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
295011868a93SSepherosa Ziehau 	ss->tx.req_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
295111868a93SSepherosa Ziehau 	/* DragonFly's kmalloc(9) promises at least 8 bytes alignment */
295211868a93SSepherosa Ziehau 	KASSERT(((uintptr_t)ss->tx.req_list & 0x7) == 0,
295311868a93SSepherosa Ziehau 	    ("req_list not 8 bytes aligned"));
29548892ea20SAggelos Economopoulos 
29557cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
29568892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
29577cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
29588892ea20SAggelos Economopoulos 
29597cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
29608892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
2961d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29628892ea20SAggelos Economopoulos 
29637cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
29648892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29658892ea20SAggelos Economopoulos 				 1,			/* alignment */
29668892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
29678892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29688892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29698892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
29707cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
29717cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
29727cc92483SSepherosa Ziehau 				 			/* maxsize */
29738892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
29748892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
29757cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
29767cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
29778892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
29788892ea20SAggelos Economopoulos 	if (err != 0) {
29797cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
29803598cc14SSascha Wildner 		return err;
29818892ea20SAggelos Economopoulos 	}
29828892ea20SAggelos Economopoulos 
29837cc92483SSepherosa Ziehau 	/*
29847cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
29857cc92483SSepherosa Ziehau 	 */
29868892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
29877cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
29887cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
29898892ea20SAggelos Economopoulos 		if (err != 0) {
2990798c3369SSepherosa Ziehau 			int j;
2991798c3369SSepherosa Ziehau 
29927cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
2993798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2994798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
2995798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
2996798c3369SSepherosa Ziehau 			}
2997798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
2998798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
29993598cc14SSascha Wildner 			return err;
30008892ea20SAggelos Economopoulos 		}
30018892ea20SAggelos Economopoulos 	}
30028892ea20SAggelos Economopoulos 	return 0;
30038892ea20SAggelos Economopoulos }
30048892ea20SAggelos Economopoulos 
30058892ea20SAggelos Economopoulos static int
30068892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
30078892ea20SAggelos Economopoulos {
30088892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30098892ea20SAggelos Economopoulos 	int tx_ring_size;
30108892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
30118892ea20SAggelos Economopoulos 	int err, slice;
30128892ea20SAggelos Economopoulos 
30137cc92483SSepherosa Ziehau 	/* Get ring sizes */
30148892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30158892ea20SAggelos Economopoulos 	if (err != 0) {
30168892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3017798c3369SSepherosa Ziehau 		return err;
30188892ea20SAggelos Economopoulos 	}
30197cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
30208892ea20SAggelos Economopoulos 
30218892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
30228892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof(mcp_dma_addr_t);
3023f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3024f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
30258892ea20SAggelos Economopoulos 
30268892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
30278892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30287cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3029798c3369SSepherosa Ziehau 		if (err != 0) {
3030798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3031798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3032798c3369SSepherosa Ziehau 			return err;
3033798c3369SSepherosa Ziehau 		}
30348892ea20SAggelos Economopoulos 	}
30358892ea20SAggelos Economopoulos 	return 0;
30368892ea20SAggelos Economopoulos }
30378892ea20SAggelos Economopoulos 
30388892ea20SAggelos Economopoulos static void
3039b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
30408892ea20SAggelos Economopoulos {
3041b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
30428892ea20SAggelos Economopoulos 
30438892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
30448892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3045b9a8961fSSepherosa Ziehau 	} else {
3046b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
30478892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
30488892ea20SAggelos Economopoulos 	}
30498892ea20SAggelos Economopoulos }
30508892ea20SAggelos Economopoulos 
30518892ea20SAggelos Economopoulos static int
3052b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
30538892ea20SAggelos Economopoulos {
30548892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30558892ea20SAggelos Economopoulos 	int err, i, slice;
30568892ea20SAggelos Economopoulos 
3057308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
30588892ea20SAggelos Economopoulos 
3059308781adSSepherosa Ziehau 	/*
3060308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3061308781adSSepherosa Ziehau 	 */
30628892ea20SAggelos Economopoulos 	err = 0;
30638892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30648892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
30658892ea20SAggelos Economopoulos 	if (slice == 0) {
30668892ea20SAggelos Economopoulos #endif
30678892ea20SAggelos Economopoulos 		cmd.data0 = slice;
3068308781adSSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3069308781adSSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3070308781adSSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
30718892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
3072308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
30738892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
3074308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
30758892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30768892ea20SAggelos Economopoulos 	}
30778892ea20SAggelos Economopoulos #endif
3078308781adSSepherosa Ziehau 
30798892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3080308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
30818892ea20SAggelos Economopoulos 	ss->rx_small.lanai =
3082308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3083308781adSSepherosa Ziehau 
30848892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3085308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
30868892ea20SAggelos Economopoulos 	ss->rx_big.lanai =
3087308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
30888892ea20SAggelos Economopoulos 
30898892ea20SAggelos Economopoulos 	if (err != 0) {
3090308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
30918892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
30928892ea20SAggelos Economopoulos 		return EIO;
30938892ea20SAggelos Economopoulos 	}
30948892ea20SAggelos Economopoulos 
3095308781adSSepherosa Ziehau 	/*
3096308781adSSepherosa Ziehau 	 * Stock small receive ring
3097308781adSSepherosa Ziehau 	 */
30988892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
30998ebf015eSSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_small,
31008ebf015eSSepherosa Ziehau 		    ss->rx_small.info[i].map, i, TRUE);
31018892ea20SAggelos Economopoulos 		if (err) {
3102308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
3103308781adSSepherosa Ziehau 			    ss->rx_small.mask + 1);
31048892ea20SAggelos Economopoulos 			return ENOMEM;
31058892ea20SAggelos Economopoulos 		}
31068892ea20SAggelos Economopoulos 	}
3107308781adSSepherosa Ziehau 
3108308781adSSepherosa Ziehau 	/*
3109308781adSSepherosa Ziehau 	 * Stock big receive ring
3110308781adSSepherosa Ziehau 	 */
31118892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
31128892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
31138892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
31148892ea20SAggelos Economopoulos 	}
3115308781adSSepherosa Ziehau 
31168892ea20SAggelos Economopoulos 	ss->rx_big.cl_size = cl_size;
31178892ea20SAggelos Economopoulos 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
3118b915556eSAggelos Economopoulos 	    EVL_ENCAPLEN + MXGEFW_PAD;
3119308781adSSepherosa Ziehau 
3120b9a8961fSSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
3121363b44f8SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_big,
3122363b44f8SSepherosa Ziehau 		    ss->rx_big.info[i].map, i, TRUE);
31238892ea20SAggelos Economopoulos 		if (err) {
3124308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
3125308781adSSepherosa Ziehau 			    ss->rx_big.mask + 1);
31268892ea20SAggelos Economopoulos 			return ENOMEM;
31278892ea20SAggelos Economopoulos 		}
31288892ea20SAggelos Economopoulos 	}
31298892ea20SAggelos Economopoulos 	return 0;
31308892ea20SAggelos Economopoulos }
31318892ea20SAggelos Economopoulos 
31328892ea20SAggelos Economopoulos static int
31338892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
31348892ea20SAggelos Economopoulos {
31356ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
31368892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3137b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
31388892ea20SAggelos Economopoulos 	bus_addr_t bus;
31398892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
31408892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
31418892ea20SAggelos Economopoulos 
31426ee6cba3SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
31436ee6cba3SSepherosa Ziehau 
31448892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
31456ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
31468892ea20SAggelos Economopoulos 
31478892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
31488892ea20SAggelos Economopoulos 	if (err != 0) {
31496ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
31508892ea20SAggelos Economopoulos 		return EIO;
31518892ea20SAggelos Economopoulos 	}
31528892ea20SAggelos Economopoulos 
31538892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
31546ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
31558892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
31566ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
31578892ea20SAggelos Economopoulos 
31586ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
31598892ea20SAggelos Economopoulos 		if (err != 0) {
31606ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
31618892ea20SAggelos Economopoulos 			return err;
31628892ea20SAggelos Economopoulos 		}
31638892ea20SAggelos Economopoulos 
31646ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
31658892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
31668892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
31678892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
31688892ea20SAggelos Economopoulos 
31698892ea20SAggelos Economopoulos 		cmd.data0 = 1;
31706ee6cba3SSepherosa Ziehau 		cmd.data1 = MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
31718892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
31728892ea20SAggelos Economopoulos 		if (err != 0) {
31736ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
31748892ea20SAggelos Economopoulos 			return err;
31758892ea20SAggelos Economopoulos 		}
31768892ea20SAggelos Economopoulos 	}
31778892ea20SAggelos Economopoulos 
317889d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
317989d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
318089d55360SSepherosa Ziehau 	if (err) {
31816ee6cba3SSepherosa Ziehau 		/*
31826ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
31836ee6cba3SSepherosa Ziehau 		 */
31846ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
31856ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
31866ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
31876ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
318889d55360SSepherosa Ziehau 	}
31898892ea20SAggelos Economopoulos 
3190b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
31918892ea20SAggelos Economopoulos 
3192b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
31936ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
31946ee6cba3SSepherosa Ziehau 	/*
31956ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
31966ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
31976ee6cba3SSepherosa Ziehau 	 */
31986ee6cba3SSepherosa Ziehau 
31996ee6cba3SSepherosa Ziehau 	/*
32006ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
32016ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
32022f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
32036ee6cba3SSepherosa Ziehau 	 */
32046ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
32058892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
32066ee6cba3SSepherosa Ziehau 
3207b9a8961fSSepherosa Ziehau 	/* XXX need to cut MXGEFW_PAD here? */
32088892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
32096ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
32106ee6cba3SSepherosa Ziehau 
3211b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
32128892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32138892ea20SAggelos Economopoulos 
32148892ea20SAggelos Economopoulos 	if (err != 0) {
32156ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32168892ea20SAggelos Economopoulos 		goto abort;
32178892ea20SAggelos Economopoulos 	}
32188892ea20SAggelos Economopoulos 
32198892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
32208892ea20SAggelos Economopoulos 	for (slice = 0;
32218892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
32228892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
32238892ea20SAggelos Economopoulos #else
32248892ea20SAggelos Economopoulos 	     slice < 1;
32258892ea20SAggelos Economopoulos #endif
32268892ea20SAggelos Economopoulos 	     slice++) {
32278892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
32287cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32297cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32308892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
32318892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
32328892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32338892ea20SAggelos Economopoulos 	}
32348892ea20SAggelos Economopoulos 
32358892ea20SAggelos Economopoulos 	if (err != 0) {
32367cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
32378892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
32388892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
32398892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
32406ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
32418892ea20SAggelos Economopoulos 		    &cmd);
32426ee6cba3SSepherosa Ziehau 
32438892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
32448892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
32458892ea20SAggelos Economopoulos 	} else {
32468892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
32478892ea20SAggelos Economopoulos 	}
32488892ea20SAggelos Economopoulos 
32498892ea20SAggelos Economopoulos 	if (err != 0) {
32506ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32518892ea20SAggelos Economopoulos 		goto abort;
32528892ea20SAggelos Economopoulos 	}
32538892ea20SAggelos Economopoulos 
32548892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3255b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
32568892ea20SAggelos Economopoulos 		if (err != 0) {
32576ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
32588892ea20SAggelos Economopoulos 			goto abort;
32598892ea20SAggelos Economopoulos 		}
32608892ea20SAggelos Economopoulos 	}
32618892ea20SAggelos Economopoulos 
32628892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
32638892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
32648892ea20SAggelos Economopoulos 	if (err) {
32656ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
32668892ea20SAggelos Economopoulos 		goto abort;
32678892ea20SAggelos Economopoulos 	}
32686ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
32696ee6cba3SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3270ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
32718892ea20SAggelos Economopoulos 
32728892ea20SAggelos Economopoulos 	return 0;
32738892ea20SAggelos Economopoulos 
32748892ea20SAggelos Economopoulos abort:
32758892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
32768892ea20SAggelos Economopoulos 	return err;
32778892ea20SAggelos Economopoulos }
32788892ea20SAggelos Economopoulos 
32792c29ffc6SSepherosa Ziehau static void
328089d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
32818892ea20SAggelos Economopoulos {
32822c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
32838892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32848892ea20SAggelos Economopoulos 	int err, old_down_cnt;
32858892ea20SAggelos Economopoulos 
32862c29ffc6SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
328789d55360SSepherosa Ziehau 
32882c29ffc6SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
32892c29ffc6SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3290ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
32912c29ffc6SSepherosa Ziehau 
329289d55360SSepherosa Ziehau 	if (!down) {
32938892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
32948892ea20SAggelos Economopoulos 		wmb();
32952c29ffc6SSepherosa Ziehau 
32968892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
32972c29ffc6SSepherosa Ziehau 		if (err)
32982c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
32992c29ffc6SSepherosa Ziehau 
33008892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
33012c29ffc6SSepherosa Ziehau 			/* Wait for down irq */
33022c29ffc6SSepherosa Ziehau 			lwkt_serialize_exit(ifp->if_serializer);
33038892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
33042c29ffc6SSepherosa Ziehau 			lwkt_serialize_enter(ifp->if_serializer);
33058892ea20SAggelos Economopoulos 		}
33062c29ffc6SSepherosa Ziehau 
33078892ea20SAggelos Economopoulos 		wmb();
33082c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
33092c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
331089d55360SSepherosa Ziehau 	}
33118892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33128892ea20SAggelos Economopoulos }
33138892ea20SAggelos Economopoulos 
33148892ea20SAggelos Economopoulos static void
33158892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
33168892ea20SAggelos Economopoulos {
33178892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33188892ea20SAggelos Economopoulos 	int reg;
331989d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
33208892ea20SAggelos Economopoulos 
33217cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
33228892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
33238892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
33248892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
33258892ea20SAggelos Economopoulos 
332689d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
33278892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
33288892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
33298892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
333089d55360SSepherosa Ziehau 			sc->pectl = pectl;
333189d55360SSepherosa Ziehau 		} else {
33327cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
333389d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
333489d55360SSepherosa Ziehau 		}
33358892ea20SAggelos Economopoulos 	}
33368892ea20SAggelos Economopoulos 
33377cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
33388892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
33398892ea20SAggelos Economopoulos }
33408892ea20SAggelos Economopoulos 
33418892ea20SAggelos Economopoulos static uint32_t
33428892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
33438892ea20SAggelos Economopoulos {
33448892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33458892ea20SAggelos Economopoulos 	uint32_t vs;
33468892ea20SAggelos Economopoulos 
33478a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
33488892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
33498a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
33508892ea20SAggelos Economopoulos 		return (uint32_t)-1;
33518892ea20SAggelos Economopoulos 	}
33528a20b038SSepherosa Ziehau 	/* Enable read32 mode */
33538892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
33548a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
33558892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
33568a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
33578892ea20SAggelos Economopoulos }
33588892ea20SAggelos Economopoulos 
335989d55360SSepherosa Ziehau static void
336089d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
33618892ea20SAggelos Economopoulos {
33628892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
336389d55360SSepherosa Ziehau 	int err, running;
33648892ea20SAggelos Economopoulos 	uint32_t reboot;
33658892ea20SAggelos Economopoulos 	uint16_t cmd;
33668892ea20SAggelos Economopoulos 
33678892ea20SAggelos Economopoulos 	err = ENXIO;
33688892ea20SAggelos Economopoulos 
33698a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
33708892ea20SAggelos Economopoulos 
33718892ea20SAggelos Economopoulos 	/*
33728a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
33738892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
33748892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
33758892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
33768892ea20SAggelos Economopoulos 	 * again
33778892ea20SAggelos Economopoulos 	 */
33788892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33798892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
33808892ea20SAggelos Economopoulos 		/*
33818a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
33828892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
33838892ea20SAggelos Economopoulos 		 * back, then give up
33848892ea20SAggelos Economopoulos 		 */
33858892ea20SAggelos Economopoulos 		DELAY(1000*100);
33868892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33878a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
33888a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
33898892ea20SAggelos Economopoulos 	}
33908892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
33918a20b038SSepherosa Ziehau 		/* Print the reboot status */
33928892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
33938a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
33948a20b038SSepherosa Ziehau 
339589d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
339689d55360SSepherosa Ziehau 		if (running) {
339789d55360SSepherosa Ziehau 			/*
33988a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
339989d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
340089d55360SSepherosa Ziehau 			 */
340189d55360SSepherosa Ziehau 
340289d55360SSepherosa Ziehau 			/* Mark the link as down */
340389d55360SSepherosa Ziehau 			if (sc->link_state) {
340489d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
340589d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
340689d55360SSepherosa Ziehau 			}
340789d55360SSepherosa Ziehau 			mxge_close(sc, 1);
340889d55360SSepherosa Ziehau 		}
34098a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
34108892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
34118892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
34128892ea20SAggelos Economopoulos 
34138a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
34148892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
34158892ea20SAggelos Economopoulos 
34168a20b038SSepherosa Ziehau 		/* Reload f/w */
341789d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
34188a20b038SSepherosa Ziehau 		if (err)
34198a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
34208a20b038SSepherosa Ziehau 		if (running && !err) {
342189d55360SSepherosa Ziehau 			err = mxge_open(sc);
342289d55360SSepherosa Ziehau 			if_devstart_sched(sc->ifp);
342389d55360SSepherosa Ziehau 		}
342489d55360SSepherosa Ziehau 		sc->watchdog_resets++;
342589d55360SSepherosa Ziehau 	} else {
34268a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
342789d55360SSepherosa Ziehau 		err = 0;
342889d55360SSepherosa Ziehau 	}
342989d55360SSepherosa Ziehau 	if (err) {
34308a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
343189d55360SSepherosa Ziehau 	} else {
343289d55360SSepherosa Ziehau 		if (sc->dying == 2)
343389d55360SSepherosa Ziehau 			sc->dying = 0;
343489d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
343589d55360SSepherosa Ziehau 	}
343689d55360SSepherosa Ziehau }
343789d55360SSepherosa Ziehau 
343889d55360SSepherosa Ziehau static void
343989d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
344089d55360SSepherosa Ziehau {
34418a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
34428a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
34438892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
34448a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
34458892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
34468a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
34478a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
34488892ea20SAggelos Economopoulos }
34498892ea20SAggelos Economopoulos 
345089d55360SSepherosa Ziehau static u_long
34518892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
34528892ea20SAggelos Economopoulos {
34538892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
345489d55360SSepherosa Ziehau 	u_long pkts = 0;
345589d55360SSepherosa Ziehau 	u_long ipackets = 0, old_ipackets;
345689d55360SSepherosa Ziehau 	u_long opackets = 0, old_opackets;
34578892ea20SAggelos Economopoulos 	int slice;
34588892ea20SAggelos Economopoulos 
34598892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
34608892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
34618892ea20SAggelos Economopoulos 		ipackets += ss->ipackets;
34628892ea20SAggelos Economopoulos 		opackets += ss->opackets;
34638892ea20SAggelos Economopoulos 	}
346489d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, old_ipackets);
346589d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, old_opackets);
346689d55360SSepherosa Ziehau 
346789d55360SSepherosa Ziehau 	pkts = ipackets - old_ipackets;
346889d55360SSepherosa Ziehau 	pkts += opackets - old_opackets;
346989d55360SSepherosa Ziehau 
3470d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, ipackets, ipackets);
3471d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, opackets, opackets);
347289d55360SSepherosa Ziehau 	return pkts;
34738892ea20SAggelos Economopoulos }
34748892ea20SAggelos Economopoulos 
34758892ea20SAggelos Economopoulos static void
34768892ea20SAggelos Economopoulos mxge_tick(void *arg)
34778892ea20SAggelos Economopoulos {
34788892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
347989d55360SSepherosa Ziehau 	u_long pkts = 0;
34808892ea20SAggelos Economopoulos 	int err = 0;
3481ca8ca004SSepherosa Ziehau 	int ticks;
348289d55360SSepherosa Ziehau 	uint16_t cmd;
34838892ea20SAggelos Economopoulos 
34842e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
348589d55360SSepherosa Ziehau 
348689d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3487ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3488ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
348989d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3490ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3491ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
349289d55360SSepherosa Ziehau 	}
349389d55360SSepherosa Ziehau 	if (pkts == 0) {
3494ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
349589d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
349689d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
349789d55360SSepherosa Ziehau 			sc->dying = 2;
349889d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
349989d55360SSepherosa Ziehau 			err = ENXIO;
350089d55360SSepherosa Ziehau 		}
3501ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
350289d55360SSepherosa Ziehau 		ticks *= 4;
350389d55360SSepherosa Ziehau 	}
350489d55360SSepherosa Ziehau 
35058892ea20SAggelos Economopoulos 	if (err == 0)
350689d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
350789d55360SSepherosa Ziehau 
35082e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
35098892ea20SAggelos Economopoulos }
35108892ea20SAggelos Economopoulos 
35118892ea20SAggelos Economopoulos static int
35128892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
35138892ea20SAggelos Economopoulos {
35148892ea20SAggelos Economopoulos 	return EINVAL;
35158892ea20SAggelos Economopoulos }
35168892ea20SAggelos Economopoulos 
35178892ea20SAggelos Economopoulos static int
35188892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
35198892ea20SAggelos Economopoulos {
35208892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
35218892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
35228892ea20SAggelos Economopoulos 	int err = 0;
35238892ea20SAggelos Economopoulos 
3524b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3525b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
35268892ea20SAggelos Economopoulos 		return EINVAL;
3527b9a8961fSSepherosa Ziehau 
35288892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
35298892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
35302ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
353189d55360SSepherosa Ziehau 		mxge_close(sc, 0);
35328892ea20SAggelos Economopoulos 		err = mxge_open(sc);
35338892ea20SAggelos Economopoulos 		if (err != 0) {
35348892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
353589d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3536b9a8961fSSepherosa Ziehau 			mxge_open(sc);
35378892ea20SAggelos Economopoulos 		}
35388892ea20SAggelos Economopoulos 	}
35398892ea20SAggelos Economopoulos 	return err;
35408892ea20SAggelos Economopoulos }
35418892ea20SAggelos Economopoulos 
35428892ea20SAggelos Economopoulos static void
35438892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
35448892ea20SAggelos Economopoulos {
35458892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
35468892ea20SAggelos Economopoulos 
35478892ea20SAggelos Economopoulos 
35488892ea20SAggelos Economopoulos 	if (sc == NULL)
35498892ea20SAggelos Economopoulos 		return;
35508892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
355189d55360SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
35528892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
355389d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
35548892ea20SAggelos Economopoulos }
35558892ea20SAggelos Economopoulos 
35568892ea20SAggelos Economopoulos static int
355789d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
355889d55360SSepherosa Ziehau     struct ucred *cr __unused)
35598892ea20SAggelos Economopoulos {
35608892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
35618892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
35628892ea20SAggelos Economopoulos 	int err, mask;
35638892ea20SAggelos Economopoulos 
356423811d63SAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
3565af85d4d5SSepherosa Ziehau 	err = 0;
3566af85d4d5SSepherosa Ziehau 
35678892ea20SAggelos Economopoulos 	switch (command) {
35688892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
35698892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
35708892ea20SAggelos Economopoulos 		break;
35718892ea20SAggelos Economopoulos 
35728892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3573af85d4d5SSepherosa Ziehau 		if (sc->dying)
35748892ea20SAggelos Economopoulos 			return EINVAL;
3575af85d4d5SSepherosa Ziehau 
35768892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
35772ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
35788892ea20SAggelos Economopoulos 				err = mxge_open(sc);
35798892ea20SAggelos Economopoulos 			} else {
3580af85d4d5SSepherosa Ziehau 				/*
3581af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3582af85d4d5SSepherosa Ziehau 				 * flag changes
3583af85d4d5SSepherosa Ziehau 				 */
35848892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
35858892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
35868892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
35878892ea20SAggelos Economopoulos 			}
35888892ea20SAggelos Economopoulos 		} else {
3589af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
359089d55360SSepherosa Ziehau 				mxge_close(sc, 0);
35918892ea20SAggelos Economopoulos 		}
35928892ea20SAggelos Economopoulos 		break;
35938892ea20SAggelos Economopoulos 
35948892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
35958892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
35968892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
35978892ea20SAggelos Economopoulos 		break;
35988892ea20SAggelos Economopoulos 
35998892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
36008892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
36018892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
360289d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
360389d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
360489d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
36058892ea20SAggelos Economopoulos 			else
360689d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
36078892ea20SAggelos Economopoulos 		}
360889d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
360989d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
361089d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
361189d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
361289d55360SSepherosa Ziehau 			else
361389d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
361489d55360SSepherosa Ziehau 		}
361589d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
361689d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
36178892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
36188892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
36198892ea20SAggelos Economopoulos 		break;
36208892ea20SAggelos Economopoulos 
36218892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
362289d55360SSepherosa Ziehau 		mxge_media_probe(sc);
36238892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
36248892ea20SAggelos Economopoulos 		    &sc->media, command);
36258892ea20SAggelos Economopoulos 		break;
36268892ea20SAggelos Economopoulos 
36278892ea20SAggelos Economopoulos 	default:
362889d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
362989d55360SSepherosa Ziehau 		break;
36308892ea20SAggelos Economopoulos 	}
36318892ea20SAggelos Economopoulos 	return err;
36328892ea20SAggelos Economopoulos }
36338892ea20SAggelos Economopoulos 
36348892ea20SAggelos Economopoulos static void
36358892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
36368892ea20SAggelos Economopoulos {
36377cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
36387cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
36397cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
36408892ea20SAggelos Economopoulos 
36417cc92483SSepherosa Ziehau 	/* XXX */
36428892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
36438892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
36447cc92483SSepherosa Ziehau 
36458892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
36468892ea20SAggelos Economopoulos 
364789d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
36487cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
36497cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
36507cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
36517cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
365289d55360SSepherosa Ziehau }
36538892ea20SAggelos Economopoulos 
36548892ea20SAggelos Economopoulos static void
36558892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
36568892ea20SAggelos Economopoulos {
36578892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
36588892ea20SAggelos Economopoulos 	int i;
36598892ea20SAggelos Economopoulos 
36608892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
36618892ea20SAggelos Economopoulos 		return;
36628892ea20SAggelos Economopoulos 
36638892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
36648892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
36658892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
36668892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
36678892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
36688892ea20SAggelos Economopoulos 		}
36698892ea20SAggelos Economopoulos 		if (ss->rx_done.entry != NULL) {
36708892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->rx_done.dma);
36718892ea20SAggelos Economopoulos 			ss->rx_done.entry = NULL;
36728892ea20SAggelos Economopoulos 		}
36738892ea20SAggelos Economopoulos 	}
36746c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
36758892ea20SAggelos Economopoulos 	sc->ss = NULL;
36768892ea20SAggelos Economopoulos }
36778892ea20SAggelos Economopoulos 
36788892ea20SAggelos Economopoulos static int
36798892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
36808892ea20SAggelos Economopoulos {
36818892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
36828892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
36838892ea20SAggelos Economopoulos 	size_t bytes;
36848892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
36858892ea20SAggelos Economopoulos 
36868892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
36878892ea20SAggelos Economopoulos 	if (err != 0) {
36888892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
36898892ea20SAggelos Economopoulos 		return err;
36908892ea20SAggelos Economopoulos 	}
36918892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
36928892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
36938892ea20SAggelos Economopoulos 
36948892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
36957cc92483SSepherosa Ziehau 	sc->ss = kmalloc(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
36967cc92483SSepherosa Ziehau 
36978892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
36988892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
36998892ea20SAggelos Economopoulos 
37008892ea20SAggelos Economopoulos 		ss->sc = sc;
37018892ea20SAggelos Economopoulos 
37027cc92483SSepherosa Ziehau 		/*
37037cc92483SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queues
37047cc92483SSepherosa Ziehau 		 */
37058892ea20SAggelos Economopoulos 		bytes = max_intr_slots * sizeof(*ss->rx_done.entry);
37068892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
3707798c3369SSepherosa Ziehau 		if (err != 0) {
3708798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3709798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3710798c3369SSepherosa Ziehau 			return err;
3711798c3369SSepherosa Ziehau 		}
37127cc92483SSepherosa Ziehau 		ss->rx_done.entry = ss->rx_done.dma.dmem_addr;
37138892ea20SAggelos Economopoulos 
37148892ea20SAggelos Economopoulos 		/*
37157cc92483SSepherosa Ziehau 		 * Allocate the per-slice firmware stats; stats
37168892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
37178892ea20SAggelos Economopoulos 		 * slice for now
37188892ea20SAggelos Economopoulos 		 */
37198892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
37208892ea20SAggelos Economopoulos 		if (i > 0)
37218892ea20SAggelos Economopoulos 			continue;
37228892ea20SAggelos Economopoulos #endif
37238892ea20SAggelos Economopoulos 
37248892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
37258892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37268892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3727798c3369SSepherosa Ziehau 		if (err != 0) {
3728798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3729798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3730798c3369SSepherosa Ziehau 			return err;
3731798c3369SSepherosa Ziehau 		}
37327cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
37338892ea20SAggelos Economopoulos 	}
37347cc92483SSepherosa Ziehau 	return 0;
37358892ea20SAggelos Economopoulos }
37368892ea20SAggelos Economopoulos 
37378892ea20SAggelos Economopoulos static void
37388892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
37398892ea20SAggelos Economopoulos {
37408892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3741c7431c78SSepherosa Ziehau 	const char *old_fw;
37428892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
37438892ea20SAggelos Economopoulos 
37448892ea20SAggelos Economopoulos 	sc->num_slices = 1;
37457cc92483SSepherosa Ziehau 
37468892ea20SAggelos Economopoulos 	/*
37477cc92483SSepherosa Ziehau 	 * XXX
37487cc92483SSepherosa Ziehau 	 *
37497cc92483SSepherosa Ziehau 	 * Don't enable multiple slices if they are not enabled,
37508892ea20SAggelos Economopoulos 	 * or if this is not an SMP system
37518892ea20SAggelos Economopoulos 	 */
3752b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
37538892ea20SAggelos Economopoulos 		return;
37548892ea20SAggelos Economopoulos 
37558892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
37568892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
37578892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
37588892ea20SAggelos Economopoulos 		return;
37598892ea20SAggelos Economopoulos 
37608892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
37618892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
37628892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
37638892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
37648892ea20SAggelos Economopoulos 	else
37658892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
37668892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
37678892ea20SAggelos Economopoulos 	if (status != 0) {
37688892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
37698892ea20SAggelos Economopoulos 		return;
37708892ea20SAggelos Economopoulos 	}
37718892ea20SAggelos Economopoulos 
37728892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
37738892ea20SAggelos Economopoulos 	   is alive */
37748892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
37758892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
37768892ea20SAggelos Economopoulos 	if (status != 0) {
37778892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
37788892ea20SAggelos Economopoulos 		goto abort_with_fw;
37798892ea20SAggelos Economopoulos 	}
37808892ea20SAggelos Economopoulos 
37818892ea20SAggelos Economopoulos 	/* get rx ring size */
37828892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37838892ea20SAggelos Economopoulos 	if (status != 0) {
37848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37858892ea20SAggelos Economopoulos 		goto abort_with_fw;
37868892ea20SAggelos Economopoulos 	}
37878892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
37888892ea20SAggelos Economopoulos 
37898892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
37908892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
37918892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
37928892ea20SAggelos Economopoulos 	if (status != 0) {
37938892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
37948892ea20SAggelos Economopoulos 		goto abort_with_fw;
37958892ea20SAggelos Economopoulos 	}
37968892ea20SAggelos Economopoulos 
37978892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
37988892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
37998892ea20SAggelos Economopoulos 	if (status != 0) {
38008892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38018892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38028892ea20SAggelos Economopoulos 		goto abort_with_fw;
38038892ea20SAggelos Economopoulos 	}
38048892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
38058892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
38068892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
38078892ea20SAggelos Economopoulos 
38088892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
38098892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
3810ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
3811ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
38128892ea20SAggelos Economopoulos 	} else {
38138892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
38148892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
38158892ea20SAggelos Economopoulos 	}
38168892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
38178892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
38188892ea20SAggelos Economopoulos 		sc->num_slices--;
38198892ea20SAggelos Economopoulos 
38207cc92483SSepherosa Ziehau 	if (bootverbose)
38218892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
38228892ea20SAggelos Economopoulos 			      sc->num_slices);
38238892ea20SAggelos Economopoulos 
38248892ea20SAggelos Economopoulos 	return;
38258892ea20SAggelos Economopoulos 
38268892ea20SAggelos Economopoulos abort_with_fw:
38278892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
38288892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
38298892ea20SAggelos Economopoulos }
38308892ea20SAggelos Economopoulos 
3831a26af990SSepherosa Ziehau #if 0
38328892ea20SAggelos Economopoulos static int
38338892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
38348892ea20SAggelos Economopoulos {
38358892ea20SAggelos Economopoulos 	size_t bytes;
38368892ea20SAggelos Economopoulos 	int count, err, i, rid;
38378892ea20SAggelos Economopoulos 
38388892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
38398892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
38408892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
38418892ea20SAggelos Economopoulos 
38428892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
38438892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
38448892ea20SAggelos Economopoulos 		return ENXIO;
38458892ea20SAggelos Economopoulos 	}
38468892ea20SAggelos Economopoulos 
38478892ea20SAggelos Economopoulos 	count = sc->num_slices;
38488892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
38498892ea20SAggelos Economopoulos 	if (err != 0) {
38508892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
38518892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
38528892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
38538892ea20SAggelos Economopoulos 	}
38548892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
38558892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
38568892ea20SAggelos Economopoulos 			      count, sc->num_slices);
38578892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38588892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
38598892ea20SAggelos Economopoulos 			      count);
38608892ea20SAggelos Economopoulos 		err = ENOSPC;
38618892ea20SAggelos Economopoulos 		goto abort_with_msix;
38628892ea20SAggelos Economopoulos 	}
38638892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
3864d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
38658892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
38668892ea20SAggelos Economopoulos 		err = ENOMEM;
38678892ea20SAggelos Economopoulos 		goto abort_with_msix;
38688892ea20SAggelos Economopoulos 	}
38698892ea20SAggelos Economopoulos 
38708892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
38718892ea20SAggelos Economopoulos 		rid = i + 1;
38728892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
38738892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
38748892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
38758892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
38768892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
38778892ea20SAggelos Economopoulos 				      " for message %d\n", i);
38788892ea20SAggelos Economopoulos 			err = ENXIO;
38798892ea20SAggelos Economopoulos 			goto abort_with_res;
38808892ea20SAggelos Economopoulos 		}
38818892ea20SAggelos Economopoulos 	}
38828892ea20SAggelos Economopoulos 
38838892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
3884d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
38858892ea20SAggelos Economopoulos 
38868892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
38878892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
38887d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
38897d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
38902e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
38918892ea20SAggelos Economopoulos 		if (err != 0) {
38928892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
38938892ea20SAggelos Economopoulos 				      "message %d\n", i);
38948892ea20SAggelos Economopoulos 			goto abort_with_intr;
38958892ea20SAggelos Economopoulos 		}
38968892ea20SAggelos Economopoulos 	}
38978892ea20SAggelos Economopoulos 
38987cc92483SSepherosa Ziehau 	if (bootverbose) {
38998892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
39008892ea20SAggelos Economopoulos 			      sc->num_slices);
39018892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
39026c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39036c348da6SAggelos Economopoulos 		kprintf("\n");
39048892ea20SAggelos Economopoulos 	}
39058892ea20SAggelos Economopoulos 	return (0);
39068892ea20SAggelos Economopoulos 
39078892ea20SAggelos Economopoulos abort_with_intr:
39088892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39098892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39108892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39118892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39128892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39138892ea20SAggelos Economopoulos 		}
39148892ea20SAggelos Economopoulos 	}
3915d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39168892ea20SAggelos Economopoulos 
39178892ea20SAggelos Economopoulos 
39188892ea20SAggelos Economopoulos abort_with_res:
39198892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39208892ea20SAggelos Economopoulos 		rid = i + 1;
39218892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39228892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39238892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39248892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39258892ea20SAggelos Economopoulos 	}
3926d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39278892ea20SAggelos Economopoulos 
39288892ea20SAggelos Economopoulos 
39298892ea20SAggelos Economopoulos abort_with_msix:
39308892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39318892ea20SAggelos Economopoulos 
39328892ea20SAggelos Economopoulos abort_with_msix_table:
39338892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39348892ea20SAggelos Economopoulos 			     sc->msix_table_res);
39358892ea20SAggelos Economopoulos 
39368892ea20SAggelos Economopoulos 	return err;
39378892ea20SAggelos Economopoulos }
3938a26af990SSepherosa Ziehau #endif
39398892ea20SAggelos Economopoulos 
39408892ea20SAggelos Economopoulos static int
39418892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
39428892ea20SAggelos Economopoulos {
394389d55360SSepherosa Ziehau 	u_int irq_flags;
394451c70c94SSascha Wildner 
39457cc92483SSepherosa Ziehau 	sc->irq_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
39467cc92483SSepherosa Ziehau 	    &sc->irq_rid, &irq_flags);
394789d55360SSepherosa Ziehau 
394889d55360SSepherosa Ziehau 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
394989d55360SSepherosa Ziehau 	    &sc->irq_rid, irq_flags);
39508892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
39518892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
39528892ea20SAggelos Economopoulos 		return ENXIO;
39538892ea20SAggelos Economopoulos 	}
395489d55360SSepherosa Ziehau 
3955798c3369SSepherosa Ziehau 	return bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE,
39567cc92483SSepherosa Ziehau 	    mxge_intr, &sc->ss[0], &sc->ih, sc->ifp->if_serializer);
39578892ea20SAggelos Economopoulos }
39588892ea20SAggelos Economopoulos 
3959a26af990SSepherosa Ziehau #if 0
39608892ea20SAggelos Economopoulos static void
39618892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
39628892ea20SAggelos Economopoulos {
39638892ea20SAggelos Economopoulos 	int i, rid;
39648892ea20SAggelos Economopoulos 
39658892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39668892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39678892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39688892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39698892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39708892ea20SAggelos Economopoulos 		}
39718892ea20SAggelos Economopoulos 	}
3972d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39738892ea20SAggelos Economopoulos 
39748892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39758892ea20SAggelos Economopoulos 		rid = i + 1;
39768892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39778892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39788892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39798892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39808892ea20SAggelos Economopoulos 	}
3981d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39828892ea20SAggelos Economopoulos 
39838892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39848892ea20SAggelos Economopoulos 			     sc->msix_table_res);
39858892ea20SAggelos Economopoulos 
39868892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39878892ea20SAggelos Economopoulos 	return;
39888892ea20SAggelos Economopoulos }
3989a26af990SSepherosa Ziehau #endif
39908892ea20SAggelos Economopoulos 
39918892ea20SAggelos Economopoulos static int
39928892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
39938892ea20SAggelos Economopoulos {
3994a26af990SSepherosa Ziehau #if 0
39958892ea20SAggelos Economopoulos 	int err;
39968892ea20SAggelos Economopoulos 
39978892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
39988892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
39998892ea20SAggelos Economopoulos 	else
40008892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
40018892ea20SAggelos Economopoulos 
40028892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
40038892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
40048892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40058892ea20SAggelos Economopoulos 	}
40068892ea20SAggelos Economopoulos 	return err;
4007a26af990SSepherosa Ziehau #else
4008a26af990SSepherosa Ziehau 	return mxge_add_single_irq(sc);
4009a26af990SSepherosa Ziehau #endif
40108892ea20SAggelos Economopoulos }
40118892ea20SAggelos Economopoulos 
40128892ea20SAggelos Economopoulos static int
40138892ea20SAggelos Economopoulos mxge_attach(device_t dev)
40148892ea20SAggelos Economopoulos {
40158892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4016137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
40178892ea20SAggelos Economopoulos 	int err, rid;
40188892ea20SAggelos Economopoulos 
4019f0115d64SAggelos Economopoulos 	/*
40207cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4021f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4022f0115d64SAggelos Economopoulos 	 */
4023f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
40248892ea20SAggelos Economopoulos 	sc->dev = dev;
40257cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
4026798c3369SSepherosa Ziehau 	ifmedia_init(&sc->media, 0, mxge_media_change, mxge_media_status);
40277cc92483SSepherosa Ziehau 
40288892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
40298892ea20SAggelos Economopoulos 
40308892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
40318892ea20SAggelos Economopoulos 				 1,			/* alignment */
40328892ea20SAggelos Economopoulos 				 0,			/* boundary */
40338892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
40348892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
40358892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
40367cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
40377cc92483SSepherosa Ziehau 				 0, 			/* num segs */
40387cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
40398892ea20SAggelos Economopoulos 				 0,			/* flags */
40408892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
40418892ea20SAggelos Economopoulos 	if (err != 0) {
4042798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4043798c3369SSepherosa Ziehau 		goto failed;
40448892ea20SAggelos Economopoulos 	}
40458892ea20SAggelos Economopoulos 
4046e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
40478892ea20SAggelos Economopoulos 
40488892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
40498892ea20SAggelos Economopoulos 
40507cc92483SSepherosa Ziehau 	/*
40517cc92483SSepherosa Ziehau 	 * Map the board into the kernel
40527cc92483SSepherosa Ziehau 	 */
40538892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
40547cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
40557cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
40568892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
40578892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
40588892ea20SAggelos Economopoulos 		err = ENXIO;
4059798c3369SSepherosa Ziehau 		goto failed;
40608892ea20SAggelos Economopoulos 	}
40617cc92483SSepherosa Ziehau 
40628892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
40638892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
40648892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
40658892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
40668892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
40678892ea20SAggelos Economopoulos 		err = ENXIO;
4068798c3369SSepherosa Ziehau 		goto failed;
40698892ea20SAggelos Economopoulos 	}
40708892ea20SAggelos Economopoulos 
40717cc92483SSepherosa Ziehau 	/*
40727cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
40737cc92483SSepherosa Ziehau 	 * lanai SRAM
40747cc92483SSepherosa Ziehau 	 */
40758892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
40768892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
40778892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
40788892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
40797cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
40808892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4081798c3369SSepherosa Ziehau 	if (err != 0) {
4082798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4083798c3369SSepherosa Ziehau 		goto failed;
4084798c3369SSepherosa Ziehau 	}
40858892ea20SAggelos Economopoulos 
40867cc92483SSepherosa Ziehau 	/*
40877cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
40887cc92483SSepherosa Ziehau 	 */
40898892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
40908892ea20SAggelos Economopoulos 
40917cc92483SSepherosa Ziehau 	/*
40927cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
40937cc92483SSepherosa Ziehau 	 */
40947cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4095798c3369SSepherosa Ziehau 	if (err != 0) {
4096798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4097798c3369SSepherosa Ziehau 		goto failed;
4098798c3369SSepherosa Ziehau 	}
40997cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
41007cc92483SSepherosa Ziehau 
41018892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4102798c3369SSepherosa Ziehau 	if (err != 0) {
4103798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4104798c3369SSepherosa Ziehau 		goto failed;
4105798c3369SSepherosa Ziehau 	}
41068892ea20SAggelos Economopoulos 
41078892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4108798c3369SSepherosa Ziehau 	if (err != 0) {
4109798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4110798c3369SSepherosa Ziehau 		goto failed;
4111798c3369SSepherosa Ziehau 	}
41128892ea20SAggelos Economopoulos 
41137cc92483SSepherosa Ziehau 	/* Select & load the firmware */
41148892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4115798c3369SSepherosa Ziehau 	if (err != 0) {
4116798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4117798c3369SSepherosa Ziehau 		goto failed;
4118798c3369SSepherosa Ziehau 	}
41198892ea20SAggelos Economopoulos 
41208892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
41218892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4122798c3369SSepherosa Ziehau 	if (err != 0) {
4123798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4124798c3369SSepherosa Ziehau 		goto failed;
4125798c3369SSepherosa Ziehau 	}
41268892ea20SAggelos Economopoulos 
41278892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4128798c3369SSepherosa Ziehau 	if (err != 0) {
4129798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4130798c3369SSepherosa Ziehau 		goto failed;
4131798c3369SSepherosa Ziehau 	}
41328892ea20SAggelos Economopoulos 
41338892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
41348892ea20SAggelos Economopoulos 	if (err != 0) {
4135798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4136798c3369SSepherosa Ziehau 		goto failed;
41378892ea20SAggelos Economopoulos 	}
41388892ea20SAggelos Economopoulos 
41398892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
414089d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
41418892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
414289d55360SSepherosa Ziehau 
414389d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
414489d55360SSepherosa Ziehau #if 0
414589d55360SSepherosa Ziehau 	/* Well, its software, sigh */
414689d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
414789d55360SSepherosa Ziehau #endif
41488892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
414989d55360SSepherosa Ziehau 
41508892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
41518892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
415289d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
41538892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
41548892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
4155ca8ca004SSepherosa Ziehau 	ifp->if_watchdog = mxge_watchdog;
415689d55360SSepherosa Ziehau 
4157820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4158820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4159820e213fSSepherosa Ziehau 
41608892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
416189d55360SSepherosa Ziehau 	mxge_media_init(sc);
41628892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
416389d55360SSepherosa Ziehau 
4164cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
416589d55360SSepherosa Ziehau 
4166b9a8961fSSepherosa Ziehau 	/*
4167b9a8961fSSepherosa Ziehau 	 * XXX
4168b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4169b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4170b9a8961fSSepherosa Ziehau 	 */
4171b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4172b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
417389d55360SSepherosa Ziehau 	sc->dying = 0;
417489d55360SSepherosa Ziehau 
4175369c353eSAggelos Economopoulos 	/* must come after ether_ifattach() */
4176369c353eSAggelos Economopoulos 	err = mxge_add_irq(sc);
4177369c353eSAggelos Economopoulos 	if (err != 0) {
4178798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4179798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4180798c3369SSepherosa Ziehau 		goto failed;
4181369c353eSAggelos Economopoulos 	}
418289d55360SSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->irq_res));
41838892ea20SAggelos Economopoulos 
41848892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
418589d55360SSepherosa Ziehau 
418689d55360SSepherosa Ziehau 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
41878892ea20SAggelos Economopoulos 	return 0;
41888892ea20SAggelos Economopoulos 
4189798c3369SSepherosa Ziehau failed:
4190798c3369SSepherosa Ziehau 	mxge_detach(dev);
41918892ea20SAggelos Economopoulos 	return err;
41928892ea20SAggelos Economopoulos }
41938892ea20SAggelos Economopoulos 
41948892ea20SAggelos Economopoulos static int
41958892ea20SAggelos Economopoulos mxge_detach(device_t dev)
41968892ea20SAggelos Economopoulos {
41978892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
41988892ea20SAggelos Economopoulos 
4199798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4200798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
4201798c3369SSepherosa Ziehau 
4202798c3369SSepherosa Ziehau 		lwkt_serialize_enter(ifp->if_serializer);
4203798c3369SSepherosa Ziehau 
42048892ea20SAggelos Economopoulos 		sc->dying = 1;
4205798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
420689d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4207e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4208798c3369SSepherosa Ziehau 
4209798c3369SSepherosa Ziehau 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
4210798c3369SSepherosa Ziehau 
4211798c3369SSepherosa Ziehau 		lwkt_serialize_exit(ifp->if_serializer);
4212e3dc37faSAggelos Economopoulos 
421389d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
421489d55360SSepherosa Ziehau 
4215798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4216798c3369SSepherosa Ziehau 	}
42178892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4218798c3369SSepherosa Ziehau 
4219798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4220798c3369SSepherosa Ziehau 	    sc->sram != NULL)
42218892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4222798c3369SSepherosa Ziehau 
42238892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
42248892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4225798c3369SSepherosa Ziehau 
4226798c3369SSepherosa Ziehau 	/* MUST after sysctls and rings are freed */
42278892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4228798c3369SSepherosa Ziehau 
4229798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
42308892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4231798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
42328892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4233798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
42348892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4235798c3369SSepherosa Ziehau 
4236798c3369SSepherosa Ziehau 	if (sc->irq_res != NULL) {
4237798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
4238798c3369SSepherosa Ziehau 		    sc->irq_res);
4239798c3369SSepherosa Ziehau 	}
4240798c3369SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_MSI)
4241798c3369SSepherosa Ziehau 		pci_release_msi(dev);
4242798c3369SSepherosa Ziehau 
4243798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4244798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4245798c3369SSepherosa Ziehau 		    sc->mem_res);
4246798c3369SSepherosa Ziehau 	}
4247798c3369SSepherosa Ziehau 
4248798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
42498892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4250798c3369SSepherosa Ziehau 
42518892ea20SAggelos Economopoulos 	return 0;
42528892ea20SAggelos Economopoulos }
42538892ea20SAggelos Economopoulos 
42548892ea20SAggelos Economopoulos static int
42558892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
42568892ea20SAggelos Economopoulos {
42578892ea20SAggelos Economopoulos 	return 0;
42588892ea20SAggelos Economopoulos }
4259