xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision c9317e74)
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 
11889d55360SSepherosa Ziehau static device_method_t mxge_methods[] = {
1198892ea20SAggelos Economopoulos 	/* Device interface */
1208892ea20SAggelos Economopoulos 	DEVMETHOD(device_probe, mxge_probe),
1218892ea20SAggelos Economopoulos 	DEVMETHOD(device_attach, mxge_attach),
1228892ea20SAggelos Economopoulos 	DEVMETHOD(device_detach, mxge_detach),
1238892ea20SAggelos Economopoulos 	DEVMETHOD(device_shutdown, mxge_shutdown),
124d3c9c58eSSascha Wildner 	DEVMETHOD_END
1258892ea20SAggelos Economopoulos };
1268892ea20SAggelos Economopoulos 
12789d55360SSepherosa Ziehau static driver_t mxge_driver = {
1288892ea20SAggelos Economopoulos 	"mxge",
1298892ea20SAggelos Economopoulos 	mxge_methods,
1308892ea20SAggelos Economopoulos 	sizeof(mxge_softc_t),
1318892ea20SAggelos Economopoulos };
1328892ea20SAggelos Economopoulos 
1338892ea20SAggelos Economopoulos static devclass_t mxge_devclass;
1348892ea20SAggelos Economopoulos 
1358892ea20SAggelos Economopoulos /* Declare ourselves to be a child of the PCI bus.*/
136aa2b9d05SSascha Wildner DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, NULL, NULL);
1378892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, firmware, 1, 1, 1);
1388892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, zlib, 1, 1, 1);
1398892ea20SAggelos Economopoulos 
1408892ea20SAggelos Economopoulos static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1418892ea20SAggelos Economopoulos static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1422c29ffc6SSepherosa Ziehau static void mxge_close(mxge_softc_t *sc, int down);
1438892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1448892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
145ca8ca004SSepherosa Ziehau static void mxge_watchdog_reset(mxge_softc_t *sc);
146ca8ca004SSepherosa Ziehau static void mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice);
1478892ea20SAggelos Economopoulos 
1488892ea20SAggelos Economopoulos static int
1498892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1508892ea20SAggelos Economopoulos {
15124e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
15224e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
15324e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
15424e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1558892ea20SAggelos Economopoulos 
1568892ea20SAggelos Economopoulos 		switch (rev) {
1578892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1588892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1598892ea20SAggelos Economopoulos 			break;
1608892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1618892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1628892ea20SAggelos Economopoulos 			break;
1638892ea20SAggelos Economopoulos 		default:
1648892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
16524e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1668892ea20SAggelos Economopoulos 			break;
1678892ea20SAggelos Economopoulos 		}
1688892ea20SAggelos Economopoulos 		return 0;
1698892ea20SAggelos Economopoulos 	}
1708892ea20SAggelos Economopoulos 	return ENXIO;
1718892ea20SAggelos Economopoulos }
1728892ea20SAggelos Economopoulos 
1738892ea20SAggelos Economopoulos static void
1748892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1758892ea20SAggelos Economopoulos {
17689d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
1778892ea20SAggelos Economopoulos 	vm_offset_t len;
1788892ea20SAggelos Economopoulos 
1798892ea20SAggelos Economopoulos 	sc->wc = 1;
1808892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
18189d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
18289d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
1839eb279beSAggelos Economopoulos #endif
1848892ea20SAggelos Economopoulos }
1858892ea20SAggelos Economopoulos 
1868892ea20SAggelos Economopoulos static int
1877cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
1888892ea20SAggelos Economopoulos     bus_size_t alignment)
1898892ea20SAggelos Economopoulos {
1907cc92483SSepherosa Ziehau 	bus_size_t boundary;
1918892ea20SAggelos Economopoulos 	int err;
1928892ea20SAggelos Economopoulos 
1937cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
1948892ea20SAggelos Economopoulos 		boundary = 0;
1957cc92483SSepherosa Ziehau 	else
1968892ea20SAggelos Economopoulos 		boundary = 4096;
1978892ea20SAggelos Economopoulos 
1987cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
1997cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
2007cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2018892ea20SAggelos Economopoulos 	if (err != 0) {
2027cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2038892ea20SAggelos Economopoulos 		return err;
2048892ea20SAggelos Economopoulos 	}
2058892ea20SAggelos Economopoulos 	return 0;
2068892ea20SAggelos Economopoulos }
2078892ea20SAggelos Economopoulos 
2088892ea20SAggelos Economopoulos static void
2097cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2108892ea20SAggelos Economopoulos {
2117cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2127cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2137cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2148892ea20SAggelos Economopoulos }
2158892ea20SAggelos Economopoulos 
2168892ea20SAggelos Economopoulos /*
2178892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2188892ea20SAggelos Economopoulos  * SN=x\0
2198892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2208892ea20SAggelos Economopoulos  * PC=text\0
2218892ea20SAggelos Economopoulos  */
2228892ea20SAggelos Economopoulos static int
2238892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2248892ea20SAggelos Economopoulos {
225c7431c78SSepherosa Ziehau 	const char *ptr;
22689d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
22789d55360SSepherosa Ziehau 	char *endptr;
2288892ea20SAggelos Economopoulos 
2298892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2308892ea20SAggelos Economopoulos 	found_mac = 0;
23189d55360SSepherosa Ziehau 	found_sn2 = 0;
23289d55360SSepherosa Ziehau 	while (*ptr != '\0') {
23389d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
23489d55360SSepherosa Ziehau 			ptr += 4;
23589d55360SSepherosa Ziehau 			for (i = 0;;) {
23689d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
23789d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2388892ea20SAggelos Economopoulos 					goto abort;
23989d55360SSepherosa Ziehau 				ptr = endptr;
24089d55360SSepherosa Ziehau 				if (++i == 6)
24189d55360SSepherosa Ziehau 					break;
24289d55360SSepherosa Ziehau 				if (*ptr++ != ':')
24389d55360SSepherosa Ziehau 					goto abort;
24489d55360SSepherosa Ziehau 			}
2458892ea20SAggelos Economopoulos 			found_mac = 1;
24689d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2478892ea20SAggelos Economopoulos 			ptr += 3;
24889d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
24989d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
25089d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2518892ea20SAggelos Economopoulos 			ptr += 3;
25289d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25389d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
25489d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
25589d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
25689d55360SSepherosa Ziehau 			ptr += 4;
25789d55360SSepherosa Ziehau 			found_sn2 = 1;
25889d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25989d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2608892ea20SAggelos Economopoulos 		}
26189d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2628892ea20SAggelos Economopoulos 	}
2638892ea20SAggelos Economopoulos 
2648892ea20SAggelos Economopoulos 	if (found_mac)
2658892ea20SAggelos Economopoulos 		return 0;
2668892ea20SAggelos Economopoulos 
2678892ea20SAggelos Economopoulos abort:
2688892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2698892ea20SAggelos Economopoulos 	return ENXIO;
2708892ea20SAggelos Economopoulos }
2718892ea20SAggelos Economopoulos 
27289d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
27389d55360SSepherosa Ziehau 
2748892ea20SAggelos Economopoulos static void
2758892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
2768892ea20SAggelos Economopoulos {
2778892ea20SAggelos Economopoulos 	uint32_t val;
2788892ea20SAggelos Economopoulos 	unsigned long base, off;
2798892ea20SAggelos Economopoulos 	char *va, *cfgptr;
2808892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
2818892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
2828892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
2838892ea20SAggelos Economopoulos 	uint32_t *ptr32;
2848892ea20SAggelos Economopoulos 
2858892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
2868892ea20SAggelos Economopoulos 		return;
2878892ea20SAggelos Economopoulos 
2888892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
2898892ea20SAggelos Economopoulos 	if (pdev == NULL) {
2908892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
2918892ea20SAggelos Economopoulos 		return;
2928892ea20SAggelos Economopoulos 	}
2938892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
2948892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
2958892ea20SAggelos Economopoulos 
2968892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
2978892ea20SAggelos Economopoulos 		return;
2988892ea20SAggelos Economopoulos 
2998892ea20SAggelos Economopoulos 	base = 0;
3008892ea20SAggelos Economopoulos 
3018892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3028892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3038892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3048892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3058892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3068892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3078892ea20SAggelos Economopoulos 		if (mcp55 &&
3088892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3098892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3108892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3118892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3128892ea20SAggelos Economopoulos 		}
3138892ea20SAggelos Economopoulos 	}
3148892ea20SAggelos Economopoulos 	if (!base)
3158892ea20SAggelos Economopoulos 		return;
3168892ea20SAggelos Economopoulos 
3177cc92483SSepherosa Ziehau 	/*
3187cc92483SSepherosa Ziehau 	 * XXXX
3197cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3207cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3217cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3227cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3232f47b54fSSepherosa Ziehau 	 * DragonFly grows support for extended pcie config space access.
3248892ea20SAggelos Economopoulos 	 */
3258892ea20SAggelos Economopoulos #if 0
3267cc92483SSepherosa Ziehau 	/*
3277cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3287cc92483SSepherosa Ziehau 	 * config space
3297cc92483SSepherosa Ziehau 	 */
3308892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3318892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3328892ea20SAggelos Economopoulos 		val |= 0x40;
3338892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3348892ea20SAggelos Economopoulos 		return;
3358892ea20SAggelos Economopoulos 	}
3368892ea20SAggelos Economopoulos #endif
3377cc92483SSepherosa Ziehau 	/*
3387cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3398892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3408892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3418892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3428892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3438892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3448892ea20SAggelos Economopoulos 	 */
3458892ea20SAggelos Economopoulos 
3468892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3478892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3488892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3498892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3508892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3518892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3528892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3538892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3548892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3558892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3568892ea20SAggelos Economopoulos 
3577cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3587cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3598892ea20SAggelos Economopoulos 
3608892ea20SAggelos Economopoulos 	/* map it into the kernel */
3618892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3628892ea20SAggelos Economopoulos 	if (va == NULL) {
3638892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3648892ea20SAggelos Economopoulos 		return;
3658892ea20SAggelos Economopoulos 	}
3668892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3678892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3688892ea20SAggelos Economopoulos 
3698892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3708892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3718892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3728892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3738892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3748892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3758892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3768892ea20SAggelos Economopoulos 		return;
3778892ea20SAggelos Economopoulos 	}
3788892ea20SAggelos Economopoulos 
3798892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
3808892ea20SAggelos Economopoulos 	val = *ptr32;
3818892ea20SAggelos Economopoulos 
3828892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
3838892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
3848892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3858892ea20SAggelos Economopoulos 		return;
3868892ea20SAggelos Economopoulos 	}
3878892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
3888892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3897cc92483SSepherosa Ziehau 	if (bootverbose) {
3907cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
3917cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
3928892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
3937cc92483SSepherosa Ziehau 	}
3948892ea20SAggelos Economopoulos }
39589d55360SSepherosa Ziehau 
39689d55360SSepherosa Ziehau #else	/* __i386__ || __x86_64__ */
39789d55360SSepherosa Ziehau 
3988892ea20SAggelos Economopoulos static void
3998892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
4008892ea20SAggelos Economopoulos {
4017cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4028892ea20SAggelos Economopoulos }
4038892ea20SAggelos Economopoulos 
40489d55360SSepherosa Ziehau #endif
4058892ea20SAggelos Economopoulos 
4068892ea20SAggelos Economopoulos static int
4078892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4088892ea20SAggelos Economopoulos {
4098892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4107cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4118892ea20SAggelos Economopoulos 	int status;
4128892ea20SAggelos Economopoulos 	uint32_t len;
4137cc92483SSepherosa Ziehau 	const char *test = " ";
4148892ea20SAggelos Economopoulos 
4157cc92483SSepherosa Ziehau 	/*
4167cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4178892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4188892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4198892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4208892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4218892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4228892ea20SAggelos Economopoulos 	 * transfers took to complete.
4238892ea20SAggelos Economopoulos 	 */
4248892ea20SAggelos Economopoulos 
4258892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4268892ea20SAggelos Economopoulos 
4278892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4288892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4298892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4308892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4318892ea20SAggelos Economopoulos 	if (status != 0) {
4328892ea20SAggelos Economopoulos 		test = "read";
4338892ea20SAggelos Economopoulos 		goto abort;
4348892ea20SAggelos Economopoulos 	}
4357cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4367cc92483SSepherosa Ziehau 
4378892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4388892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4398892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4408892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4418892ea20SAggelos Economopoulos 	if (status != 0) {
4428892ea20SAggelos Economopoulos 		test = "write";
4438892ea20SAggelos Economopoulos 		goto abort;
4448892ea20SAggelos Economopoulos 	}
4457cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4468892ea20SAggelos Economopoulos 
4478892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4488892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4498892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4508892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4518892ea20SAggelos Economopoulos 	if (status != 0) {
4528892ea20SAggelos Economopoulos 		test = "read/write";
4538892ea20SAggelos Economopoulos 		goto abort;
4548892ea20SAggelos Economopoulos 	}
4558892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4568892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4578892ea20SAggelos Economopoulos 
4588892ea20SAggelos Economopoulos abort:
4597cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4608892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4618892ea20SAggelos Economopoulos 		    test, status);
4627cc92483SSepherosa Ziehau 	}
4638892ea20SAggelos Economopoulos 	return status;
4648892ea20SAggelos Economopoulos }
4658892ea20SAggelos Economopoulos 
4668892ea20SAggelos Economopoulos /*
4678892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4688892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4698892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4708892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4718892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4728892ea20SAggelos Economopoulos  *
4738892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4748892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4758892ea20SAggelos Economopoulos  *
4768892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
4778892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
4788892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
4798892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
4808892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
4818892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
4828892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
4838892ea20SAggelos Economopoulos  */
4848892ea20SAggelos Economopoulos static int
4858892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
4868892ea20SAggelos Economopoulos {
4878892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
4888892ea20SAggelos Economopoulos 	int reg, status;
4898892ea20SAggelos Economopoulos 	uint16_t pectl;
4908892ea20SAggelos Economopoulos 
4918892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
4927cc92483SSepherosa Ziehau 
4938892ea20SAggelos Economopoulos 	/*
4948892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
4958892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
4968892ea20SAggelos Economopoulos 	 */
4978892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
4988892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
4998892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
5007cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5018892ea20SAggelos Economopoulos 			    pectl);
5028892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5038892ea20SAggelos Economopoulos 		}
5048892ea20SAggelos Economopoulos 	}
5058892ea20SAggelos Economopoulos 
5068892ea20SAggelos Economopoulos 	/*
5077cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5088892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5098892ea20SAggelos Economopoulos 	 */
5108892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5118892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5127cc92483SSepherosa Ziehau 	if (status != 0)
5138892ea20SAggelos Economopoulos 		return status;
5148892ea20SAggelos Economopoulos 
5158892ea20SAggelos Economopoulos 	/*
5168892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5178892ea20SAggelos Economopoulos 	 */
5188892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5198892ea20SAggelos Economopoulos 
5208892ea20SAggelos Economopoulos 	/*
5218892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
52289d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5238892ea20SAggelos Economopoulos 	 */
52489d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
52589d55360SSepherosa Ziehau 		return 0;
5268892ea20SAggelos Economopoulos 
5278892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5288892ea20SAggelos Economopoulos 	if (status == 0)
5298892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5308892ea20SAggelos Economopoulos 
5318892ea20SAggelos Economopoulos 	if (status != E2BIG)
5328892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5337cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5348892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5358892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5367cc92483SSepherosa Ziehau 	}
5378892ea20SAggelos Economopoulos 	return status;
5388892ea20SAggelos Economopoulos }
5398892ea20SAggelos Economopoulos 
5408892ea20SAggelos Economopoulos static int
5418892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5428892ea20SAggelos Economopoulos {
5438892ea20SAggelos Economopoulos 	int aligned = 0;
54489d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5458892ea20SAggelos Economopoulos 
54689d55360SSepherosa Ziehau 	if (sc->throttle)
54789d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5488892ea20SAggelos Economopoulos 
54989d55360SSepherosa Ziehau 	if (force_firmware != 0) {
55089d55360SSepherosa Ziehau 		if (force_firmware == 1)
5518892ea20SAggelos Economopoulos 			aligned = 1;
5528892ea20SAggelos Economopoulos 		else
5538892ea20SAggelos Economopoulos 			aligned = 0;
5547cc92483SSepherosa Ziehau 		if (bootverbose) {
5558892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5568892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5578892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5587cc92483SSepherosa Ziehau 		}
5598892ea20SAggelos Economopoulos 		goto abort;
5608892ea20SAggelos Economopoulos 	}
5618892ea20SAggelos Economopoulos 
5627cc92483SSepherosa Ziehau 	/*
5637cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5647cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5657cc92483SSepherosa Ziehau 	 */
5668892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5677cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5687cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5698892ea20SAggelos Economopoulos 		aligned = 1;
5708892ea20SAggelos Economopoulos 		goto abort;
5718892ea20SAggelos Economopoulos 	}
5728892ea20SAggelos Economopoulos 
5737cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5748892ea20SAggelos Economopoulos 		return 0;
5758892ea20SAggelos Economopoulos 
5768892ea20SAggelos Economopoulos abort:
5778892ea20SAggelos Economopoulos 	if (aligned) {
5788892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
5798892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
5808892ea20SAggelos Economopoulos 	} else {
5818892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
5828892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
5838892ea20SAggelos Economopoulos 	}
5847cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
5858892ea20SAggelos Economopoulos }
5868892ea20SAggelos Economopoulos 
5878892ea20SAggelos Economopoulos static int
5888892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
5898892ea20SAggelos Economopoulos {
5908892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
5918a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware type: 0x%x\n",
5928892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
5938892ea20SAggelos Economopoulos 		return EIO;
5948892ea20SAggelos Economopoulos 	}
5958892ea20SAggelos Economopoulos 
5967cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
5977cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
5987cc92483SSepherosa Ziehau 	if (bootverbose)
5998a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "firmware id: %s\n", hdr->version);
6008892ea20SAggelos Economopoulos 
601b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6028892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6038892ea20SAggelos Economopoulos 
6047cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6057cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6068a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Found firmware version %s\n",
6078892ea20SAggelos Economopoulos 		    sc->fw_version);
6088a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Driver needs %d.%d\n",
6098892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6108892ea20SAggelos Economopoulos 		return EINVAL;
6118892ea20SAggelos Economopoulos 	}
6128892ea20SAggelos Economopoulos 	return 0;
6138892ea20SAggelos Economopoulos }
6148892ea20SAggelos Economopoulos 
6158892ea20SAggelos Economopoulos static void *
6168892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6178892ea20SAggelos Economopoulos {
6187cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6198892ea20SAggelos Economopoulos }
6208892ea20SAggelos Economopoulos 
6218892ea20SAggelos Economopoulos static void
6228892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6238892ea20SAggelos Economopoulos {
624d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6258892ea20SAggelos Economopoulos }
626d83c779aSSascha Wildner 
6278892ea20SAggelos Economopoulos static int
6288892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6298892ea20SAggelos Economopoulos {
630d83c779aSSascha Wildner 	z_stream zs;
631d83c779aSSascha Wildner 	char *inflate_buffer;
632d83c779aSSascha Wildner 	const struct firmware *fw;
6338892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6348892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6358892ea20SAggelos Economopoulos 	int status;
6368892ea20SAggelos Economopoulos 	unsigned int i;
63789d55360SSepherosa Ziehau 	char dummy;
6388892ea20SAggelos Economopoulos 	size_t fw_len;
6398892ea20SAggelos Economopoulos 
640d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6418892ea20SAggelos Economopoulos 	if (fw == NULL) {
6428a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Could not find firmware image %s\n",
6438892ea20SAggelos Economopoulos 		    sc->fw_name);
6448892ea20SAggelos Economopoulos 		return ENOENT;
6458892ea20SAggelos Economopoulos 	}
646d83c779aSSascha Wildner 
6477cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6488892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6498892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6508892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6518892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6528892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6538892ea20SAggelos Economopoulos 		status = EIO;
6548892ea20SAggelos Economopoulos 		goto abort_with_fw;
6558892ea20SAggelos Economopoulos 	}
6568892ea20SAggelos Economopoulos 
6577cc92483SSepherosa Ziehau 	/*
6587cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6597cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6607cc92483SSepherosa Ziehau 	 */
6618892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6627cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6638892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6648892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6658892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6668892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6678892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6688892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6698a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "zlib %d\n", status);
6708892ea20SAggelos Economopoulos 		status = EIO;
6718892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6728892ea20SAggelos Economopoulos 	}
673d83c779aSSascha Wildner 
6747cc92483SSepherosa Ziehau 	/* Check id */
6757cc92483SSepherosa Ziehau 	hdr_offset =
6767cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
6778892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
6788a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware file");
6798892ea20SAggelos Economopoulos 		status = EIO;
680d83c779aSSascha Wildner 		goto abort_with_buffer;
6818892ea20SAggelos Economopoulos 	}
682d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
6838892ea20SAggelos Economopoulos 
6848892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
6858892ea20SAggelos Economopoulos 	if (status != 0)
686d83c779aSSascha Wildner 		goto abort_with_buffer;
6878892ea20SAggelos Economopoulos 
6888892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
6898892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
6907cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
6918892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
6928892ea20SAggelos Economopoulos 		wmb();
69389d55360SSepherosa Ziehau 		dummy = *sc->sram;
6948892ea20SAggelos Economopoulos 		wmb();
6958892ea20SAggelos Economopoulos 	}
6968892ea20SAggelos Economopoulos 
6978892ea20SAggelos Economopoulos 	*limit = fw_len;
6988892ea20SAggelos Economopoulos 	status = 0;
6998892ea20SAggelos Economopoulos abort_with_buffer:
700d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7018892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7028892ea20SAggelos Economopoulos abort_with_fw:
703d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7048892ea20SAggelos Economopoulos 	return status;
7058892ea20SAggelos Economopoulos }
7068892ea20SAggelos Economopoulos 
7078892ea20SAggelos Economopoulos /*
7088892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7098892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7108892ea20SAggelos Economopoulos  */
7118892ea20SAggelos Economopoulos static void
7128892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7138892ea20SAggelos Economopoulos {
7148892ea20SAggelos Economopoulos 	char buf_bytes[72];
7158892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7168892ea20SAggelos Economopoulos 	volatile char *submit;
7178892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7188892ea20SAggelos Economopoulos 	int i;
7198892ea20SAggelos Economopoulos 
7208892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7218892ea20SAggelos Economopoulos 
7227cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7238892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7248892ea20SAggelos Economopoulos 	*confirm = 0;
7258892ea20SAggelos Economopoulos 	wmb();
7268892ea20SAggelos Economopoulos 
7277cc92483SSepherosa Ziehau 	/*
7287cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7297cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7307cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7318892ea20SAggelos Economopoulos 	 */
7327cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7337cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7348892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7358892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7368892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7377cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7387cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7398892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7408892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7418892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7428892ea20SAggelos Economopoulos 
7438892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7448892ea20SAggelos Economopoulos 
7458892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7468892ea20SAggelos Economopoulos 	wmb();
7478892ea20SAggelos Economopoulos 	DELAY(1000);
7488892ea20SAggelos Economopoulos 	wmb();
7498892ea20SAggelos Economopoulos 	i = 0;
7508892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7518892ea20SAggelos Economopoulos 		DELAY(1000);
7528892ea20SAggelos Economopoulos 		i++;
7538892ea20SAggelos Economopoulos 	}
7548892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7556ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7567cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7578892ea20SAggelos Economopoulos 	}
7588892ea20SAggelos Economopoulos }
7598892ea20SAggelos Economopoulos 
7608892ea20SAggelos Economopoulos static int
7618892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7628892ea20SAggelos Economopoulos {
7638892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7648892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7658892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7668892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7678892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7688892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7698892ea20SAggelos Economopoulos 
7706ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7718892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7728892ea20SAggelos Economopoulos 
7738892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7748892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7758892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
7768892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
7777cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7787cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7798892ea20SAggelos Economopoulos 
7808892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
7818892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
7822e8181d0SAggelos Economopoulos 
7838892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
7848892ea20SAggelos Economopoulos 	wmb();
7858892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
7868892ea20SAggelos Economopoulos 
7876ee6cba3SSepherosa Ziehau 	/*
7886ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
7896ee6cba3SSepherosa Ziehau 	 */
7908892ea20SAggelos Economopoulos 	err = EAGAIN;
7918892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
7928892ea20SAggelos Economopoulos 		wmb();
7938892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
7948892ea20SAggelos Economopoulos 		case 0:
7958892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
7968892ea20SAggelos Economopoulos 			err = 0;
7978892ea20SAggelos Economopoulos 			break;
7988892ea20SAggelos Economopoulos 		case 0xffffffff:
7998892ea20SAggelos Economopoulos 			DELAY(1000);
8008892ea20SAggelos Economopoulos 			break;
8018892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8028892ea20SAggelos Economopoulos 			err = ENOSYS;
8038892ea20SAggelos Economopoulos 			break;
8048892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8058892ea20SAggelos Economopoulos 			err = E2BIG;
8068892ea20SAggelos Economopoulos 			break;
8078892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8088892ea20SAggelos Economopoulos 			err = EBUSY;
8098892ea20SAggelos Economopoulos 			break;
81089d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
81189d55360SSepherosa Ziehau 			err = ENXIO;
81289d55360SSepherosa Ziehau 			break;
8138892ea20SAggelos Economopoulos 		default:
8146ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8158892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8168892ea20SAggelos Economopoulos 			err = ENXIO;
8178892ea20SAggelos Economopoulos 			break;
8188892ea20SAggelos Economopoulos 		}
8198892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8208892ea20SAggelos Economopoulos 			break;
8218892ea20SAggelos Economopoulos 	}
8226ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8236ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8248892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8256ee6cba3SSepherosa Ziehau 	}
8268892ea20SAggelos Economopoulos 	return err;
8278892ea20SAggelos Economopoulos }
8288892ea20SAggelos Economopoulos 
8298892ea20SAggelos Economopoulos static int
8308892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8318892ea20SAggelos Economopoulos {
8328892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8338892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8348892ea20SAggelos Economopoulos 	size_t hdr_offset;
8358892ea20SAggelos Economopoulos 	int status;
8368892ea20SAggelos Economopoulos 
8377cc92483SSepherosa Ziehau 	/*
8387cc92483SSepherosa Ziehau 	 * Find running firmware header
8397cc92483SSepherosa Ziehau 	 */
8407cc92483SSepherosa Ziehau 	hdr_offset =
8417cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8428892ea20SAggelos Economopoulos 
8438892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8448a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Running firmware has bad header offset "
8458a20b038SSepherosa Ziehau 		    "(%zu)\n", hdr_offset);
8468892ea20SAggelos Economopoulos 		return EIO;
8478892ea20SAggelos Economopoulos 	}
8488892ea20SAggelos Economopoulos 
8497cc92483SSepherosa Ziehau 	/*
8507cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8517cc92483SSepherosa Ziehau 	 * validate firmware
8527cc92483SSepherosa Ziehau 	 */
8537cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8548892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8557cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8568892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
857d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8588892ea20SAggelos Economopoulos 
8598892ea20SAggelos Economopoulos 	/*
8607cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8618892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8628892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8638892ea20SAggelos Economopoulos 	 */
8648892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8658892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8668892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8678a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Adopting fw %d.%d.%d: "
8688892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8697cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8708892ea20SAggelos Economopoulos 	}
8718892ea20SAggelos Economopoulos 
8728892ea20SAggelos Economopoulos 	return status;
8738892ea20SAggelos Economopoulos }
8748892ea20SAggelos Economopoulos 
8758892ea20SAggelos Economopoulos static int
8768892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
8778892ea20SAggelos Economopoulos {
8788892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
8798892ea20SAggelos Economopoulos 	volatile char *submit;
8808892ea20SAggelos Economopoulos 	char buf_bytes[72];
8818892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
8828892ea20SAggelos Economopoulos 	int status, i;
8838892ea20SAggelos Economopoulos 
8848892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
8858892ea20SAggelos Economopoulos 
8868892ea20SAggelos Economopoulos 	size = sc->sram_size;
8878892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
8888892ea20SAggelos Economopoulos 	if (status) {
8898892ea20SAggelos Economopoulos 		if (!adopt)
8908892ea20SAggelos Economopoulos 			return status;
8917cc92483SSepherosa Ziehau 
8927cc92483SSepherosa Ziehau 		/*
8937cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
8947cc92483SSepherosa Ziehau 		 * it is new enough
8957cc92483SSepherosa Ziehau 		 */
8968892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
8978892ea20SAggelos Economopoulos 		if (status) {
8988a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
8998892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
9008892ea20SAggelos Economopoulos 			return status;
9018892ea20SAggelos Economopoulos 		}
9028a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Successfully adopted running firmware\n");
9037cc92483SSepherosa Ziehau 
9048892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9058a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9067cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9077cc92483SSepherosa Ziehau 			     "For optimal\n");
9088a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "performance consider loading "
9097cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9108892ea20SAggelos Economopoulos 		}
9118892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9128892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9138892ea20SAggelos Economopoulos 		return 0;
9148892ea20SAggelos Economopoulos 	}
9157cc92483SSepherosa Ziehau 
9167cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9178892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9188892ea20SAggelos Economopoulos 	*confirm = 0;
9198892ea20SAggelos Economopoulos 	wmb();
9207cc92483SSepherosa Ziehau 
9217cc92483SSepherosa Ziehau 	/*
9227cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9237cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9247cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9258892ea20SAggelos Economopoulos 	 */
9268892ea20SAggelos Economopoulos 
9277cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9287cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9298892ea20SAggelos Economopoulos 
9308892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9318892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9328892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9338892ea20SAggelos Economopoulos 
9347cc92483SSepherosa Ziehau 	/*
9357cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9367cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9377cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9388892ea20SAggelos Economopoulos 	 */
9398892ea20SAggelos Economopoulos 					/* where the code starts*/
9408892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9418892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9428892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9438892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9448892ea20SAggelos Economopoulos 
9458892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9468892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9478892ea20SAggelos Economopoulos 	wmb();
9488892ea20SAggelos Economopoulos 	DELAY(1000);
9498892ea20SAggelos Economopoulos 	wmb();
9508892ea20SAggelos Economopoulos 	i = 0;
9518892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9528892ea20SAggelos Economopoulos 		DELAY(1000*10);
9538892ea20SAggelos Economopoulos 		i++;
9548892ea20SAggelos Economopoulos 	}
9558892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9568a20b038SSepherosa Ziehau 		if_printf(sc->ifp,"handoff failed (%p = 0x%x)",
9578892ea20SAggelos Economopoulos 		    confirm, *confirm);
9588892ea20SAggelos Economopoulos 		return ENXIO;
9598892ea20SAggelos Economopoulos 	}
9608892ea20SAggelos Economopoulos 	return 0;
9618892ea20SAggelos Economopoulos }
9628892ea20SAggelos Economopoulos 
9638892ea20SAggelos Economopoulos static int
9648892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9658892ea20SAggelos Economopoulos {
9668892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9678892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9688892ea20SAggelos Economopoulos 
9697cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9707cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9717cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9727cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9738892ea20SAggelos Economopoulos }
9748892ea20SAggelos Economopoulos 
9758892ea20SAggelos Economopoulos static int
9768892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
9778892ea20SAggelos Economopoulos {
9788892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9798892ea20SAggelos Economopoulos 	int status;
9808892ea20SAggelos Economopoulos 
9818892ea20SAggelos Economopoulos 	if (pause)
9827cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
9838892ea20SAggelos Economopoulos 	else
9847cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
9858892ea20SAggelos Economopoulos 	if (status) {
9865a637e78SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set flow control mode\n");
9878892ea20SAggelos Economopoulos 		return ENXIO;
9888892ea20SAggelos Economopoulos 	}
9898892ea20SAggelos Economopoulos 	sc->pause = pause;
9908892ea20SAggelos Economopoulos 	return 0;
9918892ea20SAggelos Economopoulos }
9928892ea20SAggelos Economopoulos 
9938892ea20SAggelos Economopoulos static void
9948892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
9958892ea20SAggelos Economopoulos {
9968892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9978892ea20SAggelos Economopoulos 	int status;
9988892ea20SAggelos Economopoulos 
9998892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10008892ea20SAggelos Economopoulos 		promisc = 1;
10018892ea20SAggelos Economopoulos 
10028892ea20SAggelos Economopoulos 	if (promisc)
10037cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10048892ea20SAggelos Economopoulos 	else
10057cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10067cc92483SSepherosa Ziehau 	if (status)
1007af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set promisc mode\n");
10088892ea20SAggelos Economopoulos }
10098892ea20SAggelos Economopoulos 
10108892ea20SAggelos Economopoulos static void
10118892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10128892ea20SAggelos Economopoulos {
10138892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10148892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10158892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10168892ea20SAggelos Economopoulos 	int err;
10178892ea20SAggelos Economopoulos 
10188892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10198892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10208892ea20SAggelos Economopoulos 		return;
10218892ea20SAggelos Economopoulos 
10228892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
10238892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10248892ea20SAggelos Economopoulos 	if (err != 0) {
1025af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_ENABLE_ALLMULTI, "
10268892ea20SAggelos Economopoulos 		    "error status: %d\n", err);
10278892ea20SAggelos Economopoulos 		return;
10288892ea20SAggelos Economopoulos 	}
10298892ea20SAggelos Economopoulos 
10308892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10318892ea20SAggelos Economopoulos 		return;
10328892ea20SAggelos Economopoulos 
10337cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10347cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10358892ea20SAggelos Economopoulos 		return;
10368892ea20SAggelos Economopoulos 	}
10378892ea20SAggelos Economopoulos 
10387cc92483SSepherosa Ziehau 	/* Flush all the filters */
10397cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10407cc92483SSepherosa Ziehau 	if (err != 0) {
1041af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10427cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10437cc92483SSepherosa Ziehau 		return;
10447cc92483SSepherosa Ziehau 	}
10458892ea20SAggelos Economopoulos 
10467cc92483SSepherosa Ziehau 	/*
10477cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10487cc92483SSepherosa Ziehau 	 */
1049441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10508892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10518892ea20SAggelos Economopoulos 			continue;
10527cc92483SSepherosa Ziehau 
10538892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10548892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10558892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10568892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10578892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10588892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10598892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10608892ea20SAggelos Economopoulos 		if (err != 0) {
1061af85d4d5SSepherosa Ziehau 			if_printf(ifp, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
10627cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10637cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10648892ea20SAggelos Economopoulos 			return;
10658892ea20SAggelos Economopoulos 		}
10668892ea20SAggelos Economopoulos 	}
10677cc92483SSepherosa Ziehau 
10688892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10698892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10708892ea20SAggelos Economopoulos 	if (err != 0) {
1071af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_DISABLE_ALLMULTI, "
10727cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10738892ea20SAggelos Economopoulos 	}
10748892ea20SAggelos Economopoulos }
10758892ea20SAggelos Economopoulos 
107689d55360SSepherosa Ziehau #if 0
10778892ea20SAggelos Economopoulos static int
10788892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
10798892ea20SAggelos Economopoulos {
10808892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10818892ea20SAggelos Economopoulos 	int status;
10828892ea20SAggelos Economopoulos 
10838892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
10848892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10858892ea20SAggelos Economopoulos 
10868892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
10878892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
10888892ea20SAggelos Economopoulos 	cmd.data0 = 0;
10898892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
10908892ea20SAggelos Economopoulos 			       &cmd);
10918892ea20SAggelos Economopoulos 	if (status == 0)
10928892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10938892ea20SAggelos Economopoulos 
10948892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
10958892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
10968892ea20SAggelos Economopoulos }
109789d55360SSepherosa Ziehau #endif
10988892ea20SAggelos Economopoulos 
10998892ea20SAggelos Economopoulos static int
11008892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11018892ea20SAggelos Economopoulos {
11028892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11038892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11048892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11058892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11068892ea20SAggelos Economopoulos 	int slice, status;
11078892ea20SAggelos Economopoulos 
11087cc92483SSepherosa Ziehau 	/*
11097cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11107cc92483SSepherosa Ziehau 	 * is alive
11117cc92483SSepherosa Ziehau 	 */
11128892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11138892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11148892ea20SAggelos Economopoulos 	if (status != 0) {
11156ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11168892ea20SAggelos Economopoulos 		return ENXIO;
11178892ea20SAggelos Economopoulos 	}
11188892ea20SAggelos Economopoulos 
11198892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11208892ea20SAggelos Economopoulos 
11217cc92483SSepherosa Ziehau 	/* Set the intrq size */
11228892ea20SAggelos Economopoulos 	cmd.data0 = sc->rx_ring_size;
11238892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11248892ea20SAggelos Economopoulos 
11258892ea20SAggelos Economopoulos 	/*
11268892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11278892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11288892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11298892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11308892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11318892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11328892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11338892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11348892ea20SAggelos Economopoulos 	 */
11358892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11367cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11377cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11388892ea20SAggelos Economopoulos 		if (status != 0) {
11396ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11408892ea20SAggelos Economopoulos 			return status;
11418892ea20SAggelos Economopoulos 		}
11427cc92483SSepherosa Ziehau 
11438892ea20SAggelos Economopoulos 		/*
11448892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11458892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11468892ea20SAggelos Economopoulos 		 */
11478892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11488892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
11498892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
11508892ea20SAggelos Economopoulos 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11518892ea20SAggelos Economopoulos #endif
11527cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11538892ea20SAggelos Economopoulos 		if (status != 0) {
11546ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11558892ea20SAggelos Economopoulos 			return status;
11568892ea20SAggelos Economopoulos 		}
11578892ea20SAggelos Economopoulos 	}
11588892ea20SAggelos Economopoulos 
11598892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11608892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11618892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
11629a4ae890SSepherosa Ziehau 			rx_done = &sc->ss[slice].rx_data.rx_done;
11638892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
11647cc92483SSepherosa Ziehau 			cmd.data0 =
11657cc92483SSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(rx_done->dma.dmem_busaddr);
11667cc92483SSepherosa Ziehau 			cmd.data1 =
11677cc92483SSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(rx_done->dma.dmem_busaddr);
11688892ea20SAggelos Economopoulos 			cmd.data2 = slice;
11697cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
11708892ea20SAggelos Economopoulos 			    &cmd);
11718892ea20SAggelos Economopoulos 		}
11728892ea20SAggelos Economopoulos 	}
11738892ea20SAggelos Economopoulos 
11747cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
11757cc92483SSepherosa Ziehau 	    &cmd);
11768892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11778892ea20SAggelos Economopoulos 
11788892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11798892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11808892ea20SAggelos Economopoulos 
11817cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
11828892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
11837cc92483SSepherosa Ziehau 
11848892ea20SAggelos Economopoulos 	if (status != 0) {
11856ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
11868892ea20SAggelos Economopoulos 		return status;
11878892ea20SAggelos Economopoulos 	}
11888892ea20SAggelos Economopoulos 
11898892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11908892ea20SAggelos Economopoulos 
11917cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
11927cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
11938892ea20SAggelos Economopoulos 
11948892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
11958892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
11968892ea20SAggelos Economopoulos 
11978892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
11987cc92483SSepherosa Ziehau 
11997cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12009a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.idx = 0;
12019a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.cnt = 0;
12028892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12038892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12048892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12058892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12068892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12078892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12089a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.cnt = 0;
12099a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.cnt = 0;
12107cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12117cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12128892ea20SAggelos Economopoulos 	}
12138892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12147cc92483SSepherosa Ziehau 
12158892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12168892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12178892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12188892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12197cc92483SSepherosa Ziehau 
122089d55360SSepherosa Ziehau 	if (sc->throttle) {
122189d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12227cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12236ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
122489d55360SSepherosa Ziehau 	}
12258892ea20SAggelos Economopoulos 	return status;
12268892ea20SAggelos Economopoulos }
12278892ea20SAggelos Economopoulos 
12288892ea20SAggelos Economopoulos static int
122989d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
123089d55360SSepherosa Ziehau {
123189d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
123289d55360SSepherosa Ziehau 	mxge_softc_t *sc;
123389d55360SSepherosa Ziehau 	int err;
123489d55360SSepherosa Ziehau 	unsigned int throttle;
123589d55360SSepherosa Ziehau 
123689d55360SSepherosa Ziehau 	sc = arg1;
123789d55360SSepherosa Ziehau 	throttle = sc->throttle;
123889d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12395a637e78SSepherosa Ziehau 	if (err != 0)
124089d55360SSepherosa Ziehau 		return err;
124189d55360SSepherosa Ziehau 
124289d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
124389d55360SSepherosa Ziehau 		return 0;
124489d55360SSepherosa Ziehau 
124589d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
124689d55360SSepherosa Ziehau 		return EINVAL;
124789d55360SSepherosa Ziehau 
124826634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
124989d55360SSepherosa Ziehau 
125089d55360SSepherosa Ziehau 	cmd.data0 = throttle;
125189d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
125289d55360SSepherosa Ziehau 	if (err == 0)
125389d55360SSepherosa Ziehau 		sc->throttle = throttle;
125489d55360SSepherosa Ziehau 
125526634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
125689d55360SSepherosa Ziehau 	return err;
125789d55360SSepherosa Ziehau }
125889d55360SSepherosa Ziehau 
125989d55360SSepherosa Ziehau static int
12608892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
12618892ea20SAggelos Economopoulos {
12628892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12638892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
12648892ea20SAggelos Economopoulos 	int err;
12658892ea20SAggelos Economopoulos 
12668892ea20SAggelos Economopoulos 	sc = arg1;
12678892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
12688892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
12695a637e78SSepherosa Ziehau 	if (err != 0)
12708892ea20SAggelos Economopoulos 		return err;
12715a637e78SSepherosa Ziehau 
12728892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
12738892ea20SAggelos Economopoulos 		return 0;
12748892ea20SAggelos Economopoulos 
12758892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
12768892ea20SAggelos Economopoulos 		return EINVAL;
12778892ea20SAggelos Economopoulos 
127826634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
127989d55360SSepherosa Ziehau 
12808892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
12818892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
12828892ea20SAggelos Economopoulos 
128326634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
12848892ea20SAggelos Economopoulos 	return err;
12858892ea20SAggelos Economopoulos }
12868892ea20SAggelos Economopoulos 
12878892ea20SAggelos Economopoulos static int
12888892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
12898892ea20SAggelos Economopoulos {
12908892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12918892ea20SAggelos Economopoulos 	unsigned int enabled;
12928892ea20SAggelos Economopoulos 	int err;
12938892ea20SAggelos Economopoulos 
12948892ea20SAggelos Economopoulos 	sc = arg1;
12958892ea20SAggelos Economopoulos 	enabled = sc->pause;
12968892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
12975a637e78SSepherosa Ziehau 	if (err != 0)
12988892ea20SAggelos Economopoulos 		return err;
12995a637e78SSepherosa Ziehau 
13008892ea20SAggelos Economopoulos 	if (enabled == sc->pause)
13018892ea20SAggelos Economopoulos 		return 0;
13028892ea20SAggelos Economopoulos 
130326634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
13048892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
130526634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13068892ea20SAggelos Economopoulos 
13078892ea20SAggelos Economopoulos 	return err;
13088892ea20SAggelos Economopoulos }
13098892ea20SAggelos Economopoulos 
13108892ea20SAggelos Economopoulos static int
13118892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13128892ea20SAggelos Economopoulos {
13138892ea20SAggelos Economopoulos 	int err;
13148892ea20SAggelos Economopoulos 
13158892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13168892ea20SAggelos Economopoulos 		return EFAULT;
13178892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13188892ea20SAggelos Economopoulos 	arg1 = NULL;
13198892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13208892ea20SAggelos Economopoulos 
13218892ea20SAggelos Economopoulos 	return err;
13228892ea20SAggelos Economopoulos }
13238892ea20SAggelos Economopoulos 
13248892ea20SAggelos Economopoulos static void
13258892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13268892ea20SAggelos Economopoulos {
1327798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13288892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13298892ea20SAggelos Economopoulos 		int slice;
13308892ea20SAggelos Economopoulos 
13318892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13328892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1333798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13348892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13358892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13368892ea20SAggelos Economopoulos 			}
1337798c3369SSepherosa Ziehau 		}
1338798c3369SSepherosa Ziehau 	}
1339798c3369SSepherosa Ziehau 
1340798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13418892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13428892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1343798c3369SSepherosa Ziehau 	}
1344798c3369SSepherosa Ziehau 
1345798c3369SSepherosa Ziehau 	if (sc->sysctl_tree != NULL) {
1346bbac37fbSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1347bbac37fbSAggelos Economopoulos 		sc->sysctl_tree = NULL;
13488892ea20SAggelos Economopoulos 	}
1349798c3369SSepherosa Ziehau }
13508892ea20SAggelos Economopoulos 
13518892ea20SAggelos Economopoulos static void
13528892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13538892ea20SAggelos Economopoulos {
13548892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13558892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13568892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13578892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13588892ea20SAggelos Economopoulos 	int slice;
13598892ea20SAggelos Economopoulos 	char slice_num[8];
13608892ea20SAggelos Economopoulos 
1361b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1362b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1363b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
13647cc92483SSepherosa Ziehau 	    OID_AUTO, device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "");
1365b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1366b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1367b6737651SAggelos Economopoulos 		return;
1368b6737651SAggelos Economopoulos 	}
1369b6737651SAggelos Economopoulos 
1370b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
13718892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13728892ea20SAggelos Economopoulos 
13737cc92483SSepherosa Ziehau 	/*
13747cc92483SSepherosa Ziehau 	 * Random information
13757cc92483SSepherosa Ziehau 	 */
13767cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13777cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
13788892ea20SAggelos Economopoulos 
13797cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
13807cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
13818892ea20SAggelos Economopoulos 
13827cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
13837cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
13848892ea20SAggelos Economopoulos 
13857cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
13867cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
138789d55360SSepherosa Ziehau 
13887cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
13897cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
13908892ea20SAggelos Economopoulos 
13917cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
13927cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
13938892ea20SAggelos Economopoulos 
13947cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
13957cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
13968892ea20SAggelos Economopoulos 
13977cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
13987cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
13998892ea20SAggelos Economopoulos 
14007cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14017cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14027cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14037cc92483SSepherosa Ziehau 
14047cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14057cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14067cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14077cc92483SSepherosa Ziehau 
14087cc92483SSepherosa Ziehau 	/*
14097cc92483SSepherosa Ziehau 	 * Performance related tunables
14107cc92483SSepherosa Ziehau 	 */
14117cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14127cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14137cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14147cc92483SSepherosa Ziehau 
14157cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14167cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14177cc92483SSepherosa Ziehau 	    "Transmit throttling");
14187cc92483SSepherosa Ziehau 
14197cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "flow_control_enabled",
14207cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_flow_control, "I",
14217cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14227cc92483SSepherosa Ziehau 
14237cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14247cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14257cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14267cc92483SSepherosa Ziehau 
14277cc92483SSepherosa Ziehau 	/*
14287cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14297cc92483SSepherosa Ziehau 	 * Need to swap it
14307cc92483SSepherosa Ziehau 	 */
14317cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14327cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14337cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14347cc92483SSepherosa Ziehau 
14357cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14367cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14377cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14387cc92483SSepherosa Ziehau 
14397cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14407cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14417cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14427cc92483SSepherosa Ziehau 
14437cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14447cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14457cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14467cc92483SSepherosa Ziehau 
14477cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14487cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14497cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14507cc92483SSepherosa Ziehau 
14517cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14527cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14537cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14547cc92483SSepherosa Ziehau 
14557cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14567cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14577cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14587cc92483SSepherosa Ziehau 
14597cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14607cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14617cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14627cc92483SSepherosa Ziehau 
14637cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14647cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14657cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14667cc92483SSepherosa Ziehau 
14677cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14687cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14697cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14707cc92483SSepherosa Ziehau 
14717cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14727cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14737cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14747cc92483SSepherosa Ziehau 
14757cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14767cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14777cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
14787cc92483SSepherosa Ziehau 
14797cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
14807cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
14817cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
14828892ea20SAggelos Economopoulos 
14838892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
14848892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
14857cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
14867cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1487798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1488798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1489798c3369SSepherosa Ziehau 		return;
1490798c3369SSepherosa Ziehau 	}
14918892ea20SAggelos Economopoulos 
14928892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
14938892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
14948892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
14958892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
14968892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1497b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
14987cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
14997cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1500798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1501798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1502798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1503798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1504798c3369SSepherosa Ziehau 		}
15058892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15067cc92483SSepherosa Ziehau 
15077cc92483SSepherosa Ziehau 		/*
15087cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15097cc92483SSepherosa Ziehau 		 */
15107cc92483SSepherosa Ziehau 
15117cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15129a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_small.cnt, 0, "rx_small_cnt");
15137cc92483SSepherosa Ziehau 
15147cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15159a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_big.cnt, 0, "rx_small_cnt");
15168892ea20SAggelos Economopoulos 
15178892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
15188892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
15198892ea20SAggelos Economopoulos 		if (slice > 0)
15208892ea20SAggelos Economopoulos 			continue;
15218892ea20SAggelos Economopoulos #endif
15228892ea20SAggelos Economopoulos 
15237cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15247cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15257cc92483SSepherosa Ziehau 
15267cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15277cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15287cc92483SSepherosa Ziehau 
15297cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15307cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15317cc92483SSepherosa Ziehau 
15327cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15337cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15347cc92483SSepherosa Ziehau 
15357cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15367cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15377cc92483SSepherosa Ziehau 
15387cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15397cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15408892ea20SAggelos Economopoulos 	}
15418892ea20SAggelos Economopoulos }
15428892ea20SAggelos Economopoulos 
154389d55360SSepherosa Ziehau /*
154489d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
154589d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
154689d55360SSepherosa Ziehau  */
1547ddbf91b7SSepherosa Ziehau static __inline void
15488892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15498892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15508892ea20SAggelos Economopoulos {
15518892ea20SAggelos Economopoulos 	int idx, starting_slot;
15525ca32f31SSepherosa Ziehau 
15538892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15548892ea20SAggelos Economopoulos 	while (cnt > 1) {
15558892ea20SAggelos Economopoulos 		cnt--;
15568892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15575ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15588892ea20SAggelos Economopoulos 		wmb();
15598892ea20SAggelos Economopoulos 	}
15608892ea20SAggelos Economopoulos }
15618892ea20SAggelos Economopoulos 
15628892ea20SAggelos Economopoulos /*
156389d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15648892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15658892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15668892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15678892ea20SAggelos Economopoulos  */
1568ddbf91b7SSepherosa Ziehau static __inline void
156989d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15708892ea20SAggelos Economopoulos {
15718892ea20SAggelos Economopoulos 	int idx, i;
15728892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15738892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15748892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15758892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15768892ea20SAggelos Economopoulos 	uint8_t last_flags;
15778892ea20SAggelos Economopoulos 
15788892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15798892ea20SAggelos Economopoulos 
15808892ea20SAggelos Economopoulos 	last_flags = src->flags;
15818892ea20SAggelos Economopoulos 	src->flags = 0;
15828892ea20SAggelos Economopoulos 	wmb();
15838892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
15848892ea20SAggelos Economopoulos 	srcp = src;
15858892ea20SAggelos Economopoulos 
15868892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
15875ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
15888892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
15898892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
15908892ea20SAggelos Economopoulos 			srcp += 2;
15918892ea20SAggelos Economopoulos 			dstp += 2;
15928892ea20SAggelos Economopoulos 		}
15938892ea20SAggelos Economopoulos 	} else {
15945ca32f31SSepherosa Ziehau 		/*
15955ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
15965ca32f31SSepherosa Ziehau 		 * that it is submitted below
15975ca32f31SSepherosa Ziehau 		 */
15988892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
15998892ea20SAggelos Economopoulos 		i = 0;
16008892ea20SAggelos Economopoulos 	}
16018892ea20SAggelos Economopoulos 	if (i < cnt) {
16025ca32f31SSepherosa Ziehau 		/* Submit the first request */
16038892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16048892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16058892ea20SAggelos Economopoulos 	}
16068892ea20SAggelos Economopoulos 
16075ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16088892ea20SAggelos Economopoulos 	src->flags = last_flags;
16098892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16108892ea20SAggelos Economopoulos 	src_ints+=3;
16118892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16128892ea20SAggelos Economopoulos 	dst_ints+=3;
16138892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16148892ea20SAggelos Economopoulos 	tx->req += cnt;
16158892ea20SAggelos Economopoulos 	wmb();
16168892ea20SAggelos Economopoulos }
16178892ea20SAggelos Economopoulos 
161889d55360SSepherosa Ziehau static int
161989d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
162089d55360SSepherosa Ziehau {
162189d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
162289d55360SSepherosa Ziehau 	struct mbuf *m;
162389d55360SSepherosa Ziehau 
162489d55360SSepherosa Ziehau 	m = *mp;
162589d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
162689d55360SSepherosa Ziehau 
162789d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
162889d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
162989d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
163089d55360SSepherosa Ziehau 
163189d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
163289d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
163389d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
163489d55360SSepherosa Ziehau 
163589d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
163689d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
163789d55360SSepherosa Ziehau 		if (m == NULL) {
163889d55360SSepherosa Ziehau 			*mp = NULL;
163989d55360SSepherosa Ziehau 			return ENOBUFS;
164089d55360SSepherosa Ziehau 		}
164189d55360SSepherosa Ziehau 		*mp = m;
164289d55360SSepherosa Ziehau 	}
164389d55360SSepherosa Ziehau 	return 0;
164489d55360SSepherosa Ziehau }
16458892ea20SAggelos Economopoulos 
1646ca8ca004SSepherosa Ziehau static int
16475ca32f31SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mbuf *m, int busdma_seg_cnt)
16488892ea20SAggelos Economopoulos {
16498892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16508892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16518892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16528892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16538892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16548892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16558892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
16568892ea20SAggelos Economopoulos 
16578892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16588892ea20SAggelos Economopoulos 
16595ca32f31SSepherosa Ziehau 	/*
16605ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16615ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16628892ea20SAggelos Economopoulos 	 */
166389d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
166489d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16658892ea20SAggelos Economopoulos 
16665ca32f31SSepherosa Ziehau 	/*
16675ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16685ca32f31SSepherosa Ziehau 	 */
166989d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16708892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16718892ea20SAggelos Economopoulos 
16725ca32f31SSepherosa Ziehau 	/*
16735ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16745ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
16755ca32f31SSepherosa Ziehau 	 */
16768892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16778892ea20SAggelos Economopoulos 
16788892ea20SAggelos Economopoulos 	req = tx->req_list;
16798892ea20SAggelos Economopoulos 	seg = tx->seg_list;
16808892ea20SAggelos Economopoulos 	cnt = 0;
16818892ea20SAggelos Economopoulos 	rdma_count = 0;
16825ca32f31SSepherosa Ziehau 
16835ca32f31SSepherosa Ziehau 	/*
16845ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
16855ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
16865ca32f31SSepherosa Ziehau 	 * this is equal to "count".
16878892ea20SAggelos Economopoulos 	 *
16885ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
16895ca32f31SSepherosa Ziehau 	 * segment cut.
16908892ea20SAggelos Economopoulos 	 *
16915ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
16925ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
16935ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
16945ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
16955ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
16965ca32f31SSepherosa Ziehau 	 * RDMA.
16975ca32f31SSepherosa Ziehau 	 *
16985ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
16995ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17005ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17018892ea20SAggelos Economopoulos 	 */
17028892ea20SAggelos Economopoulos 
17038892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17045ca32f31SSepherosa Ziehau 		/*
17055ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17065ca32f31SSepherosa Ziehau 		 */
17078892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17088892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17098892ea20SAggelos Economopoulos 		len = seg->ds_len;
17108892ea20SAggelos Economopoulos 
17118892ea20SAggelos Economopoulos 		while (len) {
17128892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17138892ea20SAggelos Economopoulos 			seglen = len;
17148892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17158892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17168892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17175ca32f31SSepherosa Ziehau 				/* Payload */
17188892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17198892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17208892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17218892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17225ca32f31SSepherosa Ziehau 				flags_next |=
17235ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17248892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17258892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17268892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17275ca32f31SSepherosa Ziehau 				/* Header ends */
17288892ea20SAggelos Economopoulos 				rdma_count = -1;
17298892ea20SAggelos Economopoulos 				cum_len_next = 0;
17308892ea20SAggelos Economopoulos 				seglen = -cum_len;
17318892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17328892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17338892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17348892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17358892ea20SAggelos Economopoulos 			}
17368892ea20SAggelos Economopoulos 
17378892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17388892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17398892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17408892ea20SAggelos Economopoulos 			req->pad = 0;
17418892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17428892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17438892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17445ca32f31SSepherosa Ziehau 			req->flags =
17455ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17468892ea20SAggelos Economopoulos 			low += seglen;
17478892ea20SAggelos Economopoulos 			len -= seglen;
17488892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17498892ea20SAggelos Economopoulos 			flags = flags_next;
17508892ea20SAggelos Economopoulos 			req++;
17518892ea20SAggelos Economopoulos 			cnt++;
17528892ea20SAggelos Economopoulos 			rdma_count++;
17538892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17548892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17558892ea20SAggelos Economopoulos 			else
17568892ea20SAggelos Economopoulos 				cksum_offset = 0;
17578892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17588892ea20SAggelos Economopoulos 				goto drop;
17598892ea20SAggelos Economopoulos 		}
17608892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17618892ea20SAggelos Economopoulos 		seg++;
17628892ea20SAggelos Economopoulos 	}
17638892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17648892ea20SAggelos Economopoulos 
17658892ea20SAggelos Economopoulos 	do {
17668892ea20SAggelos Economopoulos 		req--;
17678892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17688892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17698892ea20SAggelos Economopoulos 
17708892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
17718892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17728892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
17738892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
17748892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
17758892ea20SAggelos Economopoulos 		*tx->send_go = 1;
17768892ea20SAggelos Economopoulos 		tx->queue_active = 1;
17778892ea20SAggelos Economopoulos 		tx->activate++;
17788892ea20SAggelos Economopoulos 		wmb();
17798892ea20SAggelos Economopoulos 	}
17808892ea20SAggelos Economopoulos #endif
1781ca8ca004SSepherosa Ziehau 	return 0;
17828892ea20SAggelos Economopoulos 
17838892ea20SAggelos Economopoulos drop:
17848892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
17858892ea20SAggelos Economopoulos 	m_freem(m);
1786ca8ca004SSepherosa Ziehau 	return ENOBUFS;
17878892ea20SAggelos Economopoulos }
17888892ea20SAggelos Economopoulos 
1789ca8ca004SSepherosa Ziehau static int
1790cc9c62a4SSepherosa Ziehau mxge_encap(mxge_tx_ring_t *tx, struct mbuf *m)
17918892ea20SAggelos Economopoulos {
17928892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
17938892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
179489d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
17958892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
17968892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
17978892ea20SAggelos Economopoulos 
179889d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1799ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1800ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1801ca8ca004SSepherosa Ziehau 			return err;
18028892ea20SAggelos Economopoulos 	}
180389d55360SSepherosa Ziehau 
18045ca32f31SSepherosa Ziehau 	/*
18055ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18065ca32f31SSepherosa Ziehau 	 */
180789d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
180889d55360SSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, tx->info[idx].map, &m,
180989d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
181089d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
181189d55360SSepherosa Ziehau 		goto drop;
181289d55360SSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, tx->info[idx].map, BUS_DMASYNC_PREWRITE);
181389d55360SSepherosa Ziehau 	tx->info[idx].m = m;
181489d55360SSepherosa Ziehau 
18155ca32f31SSepherosa Ziehau 	/*
18165ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18175ca32f31SSepherosa Ziehau 	 */
1818ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
1819ca8ca004SSepherosa Ziehau 		return mxge_encap_tso(tx, m, cnt);
18208892ea20SAggelos Economopoulos 
18218892ea20SAggelos Economopoulos 	req = tx->req_list;
18228892ea20SAggelos Economopoulos 	cksum_offset = 0;
18238892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18248892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18258892ea20SAggelos Economopoulos 
18265ca32f31SSepherosa Ziehau 	/*
18275ca32f31SSepherosa Ziehau 	 * Checksum offloading
18285ca32f31SSepherosa Ziehau 	 */
182989d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
183089d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18318892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18328892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18338892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18348892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18358892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18368892ea20SAggelos Economopoulos 	} else {
18378892ea20SAggelos Economopoulos 		odd_flag = 0;
18388892ea20SAggelos Economopoulos 	}
18398892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18408892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18418892ea20SAggelos Economopoulos 
18425ca32f31SSepherosa Ziehau 	/*
18435ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18445ca32f31SSepherosa Ziehau 	 */
18458892ea20SAggelos Economopoulos 	cum_len = 0;
18468892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18478892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18488892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18495ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18505ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18518892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18528892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18538892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18548892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18558892ea20SAggelos Economopoulos 		else
18568892ea20SAggelos Economopoulos 			cksum_offset = 0;
18578892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18588892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18598892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18608892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18618892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18628892ea20SAggelos Economopoulos 		seg++;
18638892ea20SAggelos Economopoulos 		req++;
18648892ea20SAggelos Economopoulos 		req->flags = 0;
18658892ea20SAggelos Economopoulos 	}
18668892ea20SAggelos Economopoulos 	req--;
18675ca32f31SSepherosa Ziehau 
18685ca32f31SSepherosa Ziehau 	/*
18695ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
18705ca32f31SSepherosa Ziehau 	 */
18718892ea20SAggelos Economopoulos 	if (cum_len < 60) {
18728892ea20SAggelos Economopoulos 		req++;
1873cc9c62a4SSepherosa Ziehau 		req->addr_low = htobe32(
1874cc9c62a4SSepherosa Ziehau 		    MXGE_LOWPART_TO_U32(tx->sc->zeropad_dma.dmem_busaddr));
1875cc9c62a4SSepherosa Ziehau 		req->addr_high = htobe32(
1876cc9c62a4SSepherosa Ziehau 		    MXGE_HIGHPART_TO_U32(tx->sc->zeropad_dma.dmem_busaddr));
18778892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
18788892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
18798892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18808892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18818892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18828892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18838892ea20SAggelos Economopoulos 		cnt++;
18848892ea20SAggelos Economopoulos 	}
18858892ea20SAggelos Economopoulos 
18868892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
18878892ea20SAggelos Economopoulos #if 0
18888892ea20SAggelos Economopoulos 	/* print what the firmware will see */
18898892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18906c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
18918892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
18928892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
18938892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
18948892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
18958892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
18968892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
18978892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
18988892ea20SAggelos Economopoulos 	}
18996c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19008892ea20SAggelos Economopoulos #endif
19018892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19028892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19038892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19048892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19058892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19068892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19078892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19088892ea20SAggelos Economopoulos 		tx->activate++;
19098892ea20SAggelos Economopoulos 		wmb();
19108892ea20SAggelos Economopoulos 	}
19118892ea20SAggelos Economopoulos #endif
1912ca8ca004SSepherosa Ziehau 	return 0;
19138892ea20SAggelos Economopoulos 
19148892ea20SAggelos Economopoulos drop:
19158892ea20SAggelos Economopoulos 	m_freem(m);
1916ca8ca004SSepherosa Ziehau 	return err;
19178892ea20SAggelos Economopoulos }
19188892ea20SAggelos Economopoulos 
19198892ea20SAggelos Economopoulos static void
1920f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19218892ea20SAggelos Economopoulos {
19228892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
1923795c96bbSSepherosa Ziehau 	mxge_tx_ring_t *tx;
1924ca8ca004SSepherosa Ziehau 	int encap = 0;
19258892ea20SAggelos Economopoulos 
192626634ef8SSepherosa Ziehau 	/* XXX Only use the first slice for now */
192726634ef8SSepherosa Ziehau 	tx = &sc->ss[0].tx;
192826634ef8SSepherosa Ziehau 
1929f0a26983SSepherosa Ziehau 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
193026634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
1931795c96bbSSepherosa Ziehau 
1932795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1933795c96bbSSepherosa Ziehau 		return;
1934795c96bbSSepherosa Ziehau 
1935795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1936795c96bbSSepherosa Ziehau 		struct mbuf *m;
1937ca8ca004SSepherosa Ziehau 		int error;
1938795c96bbSSepherosa Ziehau 
1939795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1940795c96bbSSepherosa Ziehau 		if (m == NULL)
1941ca8ca004SSepherosa Ziehau 			goto done;
1942795c96bbSSepherosa Ziehau 
1943795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
1944cc9c62a4SSepherosa Ziehau 		error = mxge_encap(tx, m);
1945ca8ca004SSepherosa Ziehau 		if (!error)
1946ca8ca004SSepherosa Ziehau 			encap = 1;
1947fed54363SSepherosa Ziehau 		else
1948fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1949795c96bbSSepherosa Ziehau 	}
1950795c96bbSSepherosa Ziehau 
1951795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1952795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1953ca8ca004SSepherosa Ziehau done:
1954ca8ca004SSepherosa Ziehau 	if (encap)
1955ca8ca004SSepherosa Ziehau 		ifp->if_timer = 5;
1956ca8ca004SSepherosa Ziehau }
1957ca8ca004SSepherosa Ziehau 
1958ca8ca004SSepherosa Ziehau static void
1959ca8ca004SSepherosa Ziehau mxge_watchdog(struct ifnet *ifp)
1960ca8ca004SSepherosa Ziehau {
1961ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1962ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1963ca8ca004SSepherosa Ziehau 	mxge_tx_ring_t *tx = &sc->ss[0].tx;
1964ca8ca004SSepherosa Ziehau 
196526634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
1966ca8ca004SSepherosa Ziehau 
1967ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
1968ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
1969ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
1970ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
1971ca8ca004SSepherosa Ziehau 		return;
1972ca8ca004SSepherosa Ziehau 	} else {
1973ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
1974ca8ca004SSepherosa Ziehau 		    "check link partner\n");
1975ca8ca004SSepherosa Ziehau 	}
1976ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
19778892ea20SAggelos Economopoulos }
19788892ea20SAggelos Economopoulos 
19798892ea20SAggelos Economopoulos /*
19802f47b54fSSepherosa Ziehau  * Copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
19818892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
19828892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's low
19838892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
19848892ea20SAggelos Economopoulos  * in a burst
19858892ea20SAggelos Economopoulos  */
1986ddbf91b7SSepherosa Ziehau static __inline void
19878892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
19888892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
19898892ea20SAggelos Economopoulos {
19908892ea20SAggelos Economopoulos 	uint32_t low;
19918892ea20SAggelos Economopoulos 
19928892ea20SAggelos Economopoulos 	low = src->addr_low;
19938892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
19948892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
19958892ea20SAggelos Economopoulos 	wmb();
19968892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
19978892ea20SAggelos Economopoulos 	wmb();
19988892ea20SAggelos Economopoulos 	src->addr_low = low;
19998892ea20SAggelos Economopoulos 	dst->addr_low = low;
20008892ea20SAggelos Economopoulos 	wmb();
20018892ea20SAggelos Economopoulos }
20028892ea20SAggelos Economopoulos 
20038892ea20SAggelos Economopoulos static int
20048ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20058ebf015eSSepherosa Ziehau     boolean_t init)
20068892ea20SAggelos Economopoulos {
20078892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20088892ea20SAggelos Economopoulos 	struct mbuf *m;
2009363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20108892ea20SAggelos Economopoulos 
20118ebf015eSSepherosa Ziehau 	mflag = MB_DONTWAIT;
20128ebf015eSSepherosa Ziehau 	if (__predict_false(init))
20138ebf015eSSepherosa Ziehau 		mflag = MB_WAIT;
20148ebf015eSSepherosa Ziehau 
20158ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20168892ea20SAggelos Economopoulos 	if (m == NULL) {
20178892ea20SAggelos Economopoulos 		rx->alloc_fail++;
20188892ea20SAggelos Economopoulos 		err = ENOBUFS;
20198ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20208ebf015eSSepherosa Ziehau 			/*
20218ebf015eSSepherosa Ziehau 			 * During initialization, there
20228ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20238ebf015eSSepherosa Ziehau 			 */
20248ebf015eSSepherosa Ziehau 			return err;
20258ebf015eSSepherosa Ziehau 		}
20268892ea20SAggelos Economopoulos 		goto done;
20278892ea20SAggelos Economopoulos 	}
20282823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20298ebf015eSSepherosa Ziehau 
20307d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20317d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20328892ea20SAggelos Economopoulos 	if (err != 0) {
20338ebf015eSSepherosa Ziehau 		m_freem(m);
20348ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20358ebf015eSSepherosa Ziehau 			/*
20368ebf015eSSepherosa Ziehau 			 * During initialization, there
20378ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20388ebf015eSSepherosa Ziehau 			 */
20398ebf015eSSepherosa Ziehau 			return err;
20408ebf015eSSepherosa Ziehau 		}
20418892ea20SAggelos Economopoulos 		goto done;
20428892ea20SAggelos Economopoulos 	}
20438ebf015eSSepherosa Ziehau 
20448892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20458ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20468ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20478892ea20SAggelos Economopoulos 
20488892ea20SAggelos Economopoulos done:
20498892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20508892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20518892ea20SAggelos Economopoulos 	return err;
20528892ea20SAggelos Economopoulos }
20538892ea20SAggelos Economopoulos 
20548892ea20SAggelos Economopoulos static int
2055363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2056363b44f8SSepherosa Ziehau     boolean_t init)
20578892ea20SAggelos Economopoulos {
2058b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20598892ea20SAggelos Economopoulos 	struct mbuf *m;
2060363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2061363b44f8SSepherosa Ziehau 
2062363b44f8SSepherosa Ziehau 	mflag = MB_DONTWAIT;
2063363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2064363b44f8SSepherosa Ziehau 		mflag = MB_WAIT;
20658892ea20SAggelos Economopoulos 
20668892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2067363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2068b9a8961fSSepherosa Ziehau 	else
2069363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
20708892ea20SAggelos Economopoulos 	if (m == NULL) {
20718892ea20SAggelos Economopoulos 		rx->alloc_fail++;
20728892ea20SAggelos Economopoulos 		err = ENOBUFS;
2073363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2074363b44f8SSepherosa Ziehau 			/*
2075363b44f8SSepherosa Ziehau 			 * During initialization, there
2076363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2077363b44f8SSepherosa Ziehau 			 */
2078363b44f8SSepherosa Ziehau 			return err;
2079363b44f8SSepherosa Ziehau 		}
20808892ea20SAggelos Economopoulos 		goto done;
20818892ea20SAggelos Economopoulos 	}
20822823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = rx->mlen;
2083b9a8961fSSepherosa Ziehau 
20847d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2085b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20868892ea20SAggelos Economopoulos 	if (err != 0) {
2087363b44f8SSepherosa Ziehau 		m_freem(m);
2088363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2089363b44f8SSepherosa Ziehau 			/*
2090363b44f8SSepherosa Ziehau 			 * During initialization, there
2091363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2092363b44f8SSepherosa Ziehau 			 */
2093363b44f8SSepherosa Ziehau 			return err;
2094363b44f8SSepherosa Ziehau 		}
20958892ea20SAggelos Economopoulos 		goto done;
20968892ea20SAggelos Economopoulos 	}
2097b9a8961fSSepherosa Ziehau 
20988892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2099363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2100363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21018892ea20SAggelos Economopoulos 
21028892ea20SAggelos Economopoulos done:
2103b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2104b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21058892ea20SAggelos Economopoulos 	return err;
21068892ea20SAggelos Economopoulos }
21078892ea20SAggelos Economopoulos 
21088892ea20SAggelos Economopoulos /*
21098892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21108892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21118892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21128892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21138892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21148892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21158892ea20SAggelos Economopoulos  */
211652cf8dfcSSepherosa Ziehau static __inline uint16_t
21178892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21188892ea20SAggelos Economopoulos {
211952cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
212052cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21218892ea20SAggelos Economopoulos 	uint16_t c;
21228892ea20SAggelos Economopoulos 
212352cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21248892ea20SAggelos Economopoulos 
212552cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21268892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21278892ea20SAggelos Economopoulos 		return 1;
212852cf8dfcSSepherosa Ziehau 
212952cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
213052cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21318892ea20SAggelos Economopoulos 		return 1;
213252cf8dfcSSepherosa Ziehau 
21338892ea20SAggelos Economopoulos #ifdef INET
21348892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21358892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21368892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21378892ea20SAggelos Economopoulos #else
21388892ea20SAggelos Economopoulos 	c = 1;
21398892ea20SAggelos Economopoulos #endif
21408892ea20SAggelos Economopoulos 	c ^= 0xffff;
214152cf8dfcSSepherosa Ziehau 	return c;
21428892ea20SAggelos Economopoulos }
21438892ea20SAggelos Economopoulos 
21448892ea20SAggelos Economopoulos static void
21458892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21468892ea20SAggelos Economopoulos {
21478892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21488892ea20SAggelos Economopoulos 	uint32_t partial;
21498892ea20SAggelos Economopoulos 
21508892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21518892ea20SAggelos Economopoulos 
21528892ea20SAggelos Economopoulos 	/*
215352cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
215452cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21558892ea20SAggelos Economopoulos 	 * header.
21568892ea20SAggelos Economopoulos 	 */
21578892ea20SAggelos Economopoulos 
215852cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21598892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21608892ea20SAggelos Economopoulos 
216152cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
216252cf8dfcSSepherosa Ziehau 	*csum += ~partial;
216352cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
216452cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
216552cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
216652cf8dfcSSepherosa Ziehau 
216752cf8dfcSSepherosa Ziehau 	/*
216852cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
216952cf8dfcSSepherosa Ziehau 	 * later consumers expect this
217052cf8dfcSSepherosa Ziehau 	 */
21718892ea20SAggelos Economopoulos 	*csum = htons(*csum);
21728892ea20SAggelos Economopoulos 
21738892ea20SAggelos Economopoulos 	/* save the tag */
2174b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
21758892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
21768892ea20SAggelos Economopoulos 
21778892ea20SAggelos Economopoulos 	/*
21788892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
21798892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
21808892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
21818892ea20SAggelos Economopoulos 	 * type field is already in place.
21828892ea20SAggelos Economopoulos 	 */
2183b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
21848892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2185b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
21868892ea20SAggelos Economopoulos }
21878892ea20SAggelos Economopoulos 
21888892ea20SAggelos Economopoulos 
218952cf8dfcSSepherosa Ziehau static __inline void
2190cc9c62a4SSepherosa Ziehau mxge_rx_done_big(mxge_rx_ring_t *rx, uint32_t len, uint32_t csum)
21918892ea20SAggelos Economopoulos {
2192cc9c62a4SSepherosa Ziehau 	struct ifnet *ifp = rx->sc->ifp;
21938892ea20SAggelos Economopoulos 	struct mbuf *m;
219452cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
21958892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
21968892ea20SAggelos Economopoulos 	int idx;
21978892ea20SAggelos Economopoulos 
21988892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2199b9a8961fSSepherosa Ziehau 	rx->cnt++;
220052cf8dfcSSepherosa Ziehau 
220152cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22028892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
220352cf8dfcSSepherosa Ziehau 
220452cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2205363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
220652cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2207d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22088892ea20SAggelos Economopoulos 		return;
22098892ea20SAggelos Economopoulos 	}
22108892ea20SAggelos Economopoulos 
221152cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22128892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22138892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22148892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22158892ea20SAggelos Economopoulos 
221652cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22178892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22188892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22198892ea20SAggelos Economopoulos 
222052cf8dfcSSepherosa Ziehau 	/*
222152cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
222252cf8dfcSSepherosa Ziehau 	 * aligned
222352cf8dfcSSepherosa Ziehau 	 */
22248892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22258892ea20SAggelos Economopoulos 
22268892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22278892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
222852cf8dfcSSepherosa Ziehau 
2229cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
223052cf8dfcSSepherosa Ziehau 
223152cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
223252cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22338892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
223452cf8dfcSSepherosa Ziehau 
223552cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
223689d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
223752cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
223889d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22398892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
224089d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
224189d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22428892ea20SAggelos Economopoulos 	}
2243eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
22448892ea20SAggelos Economopoulos }
22458892ea20SAggelos Economopoulos 
224652cf8dfcSSepherosa Ziehau static __inline void
2247cc9c62a4SSepherosa Ziehau mxge_rx_done_small(mxge_rx_ring_t *rx, uint32_t len, uint32_t csum)
22488892ea20SAggelos Economopoulos {
2249cc9c62a4SSepherosa Ziehau 	struct ifnet *ifp = rx->sc->ifp;
225052cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22518892ea20SAggelos Economopoulos 	struct mbuf *m;
22528892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22538892ea20SAggelos Economopoulos 	int idx;
22548892ea20SAggelos Economopoulos 
22558892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22568892ea20SAggelos Economopoulos 	rx->cnt++;
225752cf8dfcSSepherosa Ziehau 
225852cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22598892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
226052cf8dfcSSepherosa Ziehau 
226152cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22628ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
226352cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2264d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22658892ea20SAggelos Economopoulos 		return;
22668892ea20SAggelos Economopoulos 	}
22678892ea20SAggelos Economopoulos 
226852cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22698892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22708892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22718892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22728892ea20SAggelos Economopoulos 
227352cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22748892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22758892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22768892ea20SAggelos Economopoulos 
227752cf8dfcSSepherosa Ziehau 	/*
227852cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
227952cf8dfcSSepherosa Ziehau 	 * aligned
228052cf8dfcSSepherosa Ziehau 	 */
22818892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22828892ea20SAggelos Economopoulos 
22838892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22848892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
228552cf8dfcSSepherosa Ziehau 
2286cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
228752cf8dfcSSepherosa Ziehau 
228852cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
228952cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22908892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
229152cf8dfcSSepherosa Ziehau 
229252cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
229389d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
229452cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
229589d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22968892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
229789d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
229889d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22998892ea20SAggelos Economopoulos 	}
2300eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
23018892ea20SAggelos Economopoulos }
23028892ea20SAggelos Economopoulos 
230352cf8dfcSSepherosa Ziehau static __inline void
2304cc9c62a4SSepherosa Ziehau mxge_clean_rx_done(mxge_rx_done_t *rx_done)
23058892ea20SAggelos Economopoulos {
23068892ea20SAggelos Economopoulos 	while (rx_done->entry[rx_done->idx].length != 0) {
230752cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
230852cf8dfcSSepherosa Ziehau 
23098892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23108892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
231152cf8dfcSSepherosa Ziehau 
23128892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
231352cf8dfcSSepherosa Ziehau 
23148892ea20SAggelos Economopoulos 		if (length <= (MHLEN - MXGEFW_PAD))
2315cc9c62a4SSepherosa Ziehau 			mxge_rx_done_small(rx_done->rx_small, length, checksum);
23168892ea20SAggelos Economopoulos 		else
2317cc9c62a4SSepherosa Ziehau 			mxge_rx_done_big(rx_done->rx_big, length, checksum);
231852cf8dfcSSepherosa Ziehau 
23198892ea20SAggelos Economopoulos 		rx_done->cnt++;
23208892ea20SAggelos Economopoulos 		rx_done->idx = rx_done->cnt & rx_done->mask;
23218892ea20SAggelos Economopoulos 	}
23228892ea20SAggelos Economopoulos }
23238892ea20SAggelos Economopoulos 
2324ddbf91b7SSepherosa Ziehau static __inline void
2325cc9c62a4SSepherosa Ziehau mxge_tx_done(mxge_tx_ring_t *tx, uint32_t mcp_idx)
23268892ea20SAggelos Economopoulos {
2327cc9c62a4SSepherosa Ziehau 	struct ifnet *ifp = tx->sc->ifp;
23288892ea20SAggelos Economopoulos 
232926634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
233066e7a0e8SSepherosa Ziehau 
23318892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
233266e7a0e8SSepherosa Ziehau 		struct mbuf *m;
233366e7a0e8SSepherosa Ziehau 		int idx;
233466e7a0e8SSepherosa Ziehau 
23358892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23368892ea20SAggelos Economopoulos 		tx->done++;
233766e7a0e8SSepherosa Ziehau 
23388892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
233966e7a0e8SSepherosa Ziehau 		/*
234066e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
234166e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
234266e7a0e8SSepherosa Ziehau 		 */
23438892ea20SAggelos Economopoulos 		if (m != NULL) {
2344cc9c62a4SSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
23458892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
234666e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23478892ea20SAggelos Economopoulos 			m_freem(m);
23488892ea20SAggelos Economopoulos 		}
23498892ea20SAggelos Economopoulos 		if (tx->info[idx].flag) {
23508892ea20SAggelos Economopoulos 			tx->info[idx].flag = 0;
23518892ea20SAggelos Economopoulos 			tx->pkt_done++;
23528892ea20SAggelos Economopoulos 		}
23538892ea20SAggelos Economopoulos 	}
23548892ea20SAggelos Economopoulos 
235566e7a0e8SSepherosa Ziehau 	/*
235666e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
235766e7a0e8SSepherosa Ziehau 	 * its OK to send packets
235866e7a0e8SSepherosa Ziehau 	 */
2359ca8ca004SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 4) {
23609ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
2361ca8ca004SSepherosa Ziehau 		if (tx->req == tx->done)
2362ca8ca004SSepherosa Ziehau 			ifp->if_timer = 0;
2363ca8ca004SSepherosa Ziehau 	}
236489d55360SSepherosa Ziehau 
236589d55360SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
236689d55360SSepherosa Ziehau 		if_devstart(ifp);
236789d55360SSepherosa Ziehau 
23688892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
23698892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
23708892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
23718892ea20SAggelos Economopoulos 		 * are no more transmits pending */
23728892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
23738892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
23748892ea20SAggelos Economopoulos 			tx->queue_active = 0;
23758892ea20SAggelos Economopoulos 			tx->deactivate++;
23768892ea20SAggelos Economopoulos 			wmb();
23778892ea20SAggelos Economopoulos 		}
23788892ea20SAggelos Economopoulos 	}
23798892ea20SAggelos Economopoulos #endif
23808892ea20SAggelos Economopoulos }
23818892ea20SAggelos Economopoulos 
238289d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
23838892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
23848892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
23858892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
23868892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
23878892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
23888892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
23898892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
23908892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
23918892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
23928892ea20SAggelos Economopoulos };
239389d55360SSepherosa Ziehau 
239489d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
239589d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
23968892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
23978892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
23988892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
239989d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
240089d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24018892ea20SAggelos Economopoulos };
24028892ea20SAggelos Economopoulos 
24038892ea20SAggelos Economopoulos static void
240489d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24058892ea20SAggelos Economopoulos {
24067cc92483SSepherosa Ziehau 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 0, NULL);
240789d55360SSepherosa Ziehau 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
240889d55360SSepherosa Ziehau 	sc->current_media = media_type;
240989d55360SSepherosa Ziehau 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
24108892ea20SAggelos Economopoulos }
24118892ea20SAggelos Economopoulos 
24128892ea20SAggelos Economopoulos static void
241389d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24148892ea20SAggelos Economopoulos {
2415c7431c78SSepherosa Ziehau 	const char *ptr;
241689d55360SSepherosa Ziehau 	int i;
24178892ea20SAggelos Economopoulos 
241889d55360SSepherosa Ziehau 	ifmedia_removeall(&sc->media);
241989d55360SSepherosa Ziehau 	mxge_media_set(sc, IFM_AUTO);
24208892ea20SAggelos Economopoulos 
24218892ea20SAggelos Economopoulos 	/*
24222f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24238892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24248892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24258892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24268892ea20SAggelos Economopoulos 	 */
24278892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24288892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2429af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
243089d55360SSepherosa Ziehau 		return;
24318892ea20SAggelos Economopoulos 	}
24328892ea20SAggelos Economopoulos 
24338892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
243489d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24358892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2436af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24378892ea20SAggelos Economopoulos 			return;
24388892ea20SAggelos Economopoulos 		}
24398892ea20SAggelos Economopoulos 	}
244089d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24418892ea20SAggelos Economopoulos 		/* -C is CX4 */
244289d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
244389d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
244489d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24458892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
244689d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2447af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24482f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
244989d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
245089d55360SSepherosa Ziehau 		/* -R is XFP */
245189d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
245289d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
245389d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
245489d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
245589d55360SSepherosa Ziehau 	} else {
2456af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
245789d55360SSepherosa Ziehau 	}
24588892ea20SAggelos Economopoulos }
24598892ea20SAggelos Economopoulos 
246089d55360SSepherosa Ziehau /*
246189d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
246289d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
246389d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
246489d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
246589d55360SSepherosa Ziehau  * than in the interrupt handler itself.
246689d55360SSepherosa Ziehau  */
246789d55360SSepherosa Ziehau static void
246889d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
246989d55360SSepherosa Ziehau {
247089d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
24717cc92483SSepherosa Ziehau 	const char *cage_type;
247289d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
247389d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
247489d55360SSepherosa Ziehau 	uint32_t byte;
247589d55360SSepherosa Ziehau 
247689d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
247789d55360SSepherosa Ziehau 
247889d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
24798892ea20SAggelos Economopoulos 		/* -R is XFP */
24808892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
24817cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_xfp_media_types) /
248289d55360SSepherosa Ziehau 		    sizeof(mxge_xfp_media_types[0]);
24838892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
24848892ea20SAggelos Economopoulos 		cage_type = "XFP";
248589d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
24868892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
24878892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
24887cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_sfp_media_types) /
248989d55360SSepherosa Ziehau 		    sizeof(mxge_sfp_media_types[0]);
24908892ea20SAggelos Economopoulos 		cage_type = "SFP+";
24918892ea20SAggelos Economopoulos 		byte = 3;
249289d55360SSepherosa Ziehau 	} else {
249389d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
24948892ea20SAggelos Economopoulos 		return;
24958892ea20SAggelos Economopoulos 	}
24968892ea20SAggelos Economopoulos 
24978892ea20SAggelos Economopoulos 	/*
24988892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
24998892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25008892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25018892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25028892ea20SAggelos Economopoulos 	 * a millisecond
25038892ea20SAggelos Economopoulos 	 */
25048892ea20SAggelos Economopoulos 
25058892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25068892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25078892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
25087cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2509af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read XFP\n");
25107cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2511af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
25127cc92483SSepherosa Ziehau 	if (err != MXGEFW_CMD_OK)
25138892ea20SAggelos Economopoulos 		return;
25148892ea20SAggelos Economopoulos 
25157cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25168892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25178892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25187cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25198892ea20SAggelos Economopoulos 		DELAY(1000);
25208892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25218892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25228892ea20SAggelos Economopoulos 	}
25238892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2524af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25258892ea20SAggelos Economopoulos 		    cage_type, err, ms);
25268892ea20SAggelos Economopoulos 		return;
25278892ea20SAggelos Economopoulos 	}
25288892ea20SAggelos Economopoulos 
25298892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25307cc92483SSepherosa Ziehau 		if (bootverbose) {
2531af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25328892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25337cc92483SSepherosa Ziehau 		}
253489d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
253589d55360SSepherosa Ziehau 			mxge_media_init(sc);
253689d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
253789d55360SSepherosa Ziehau 		}
25388892ea20SAggelos Economopoulos 		return;
25398892ea20SAggelos Economopoulos 	}
25408892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25418892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25427cc92483SSepherosa Ziehau 			if (bootverbose) {
2543af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25448892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25457cc92483SSepherosa Ziehau 			}
25468892ea20SAggelos Economopoulos 
254789d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
254889d55360SSepherosa Ziehau 				mxge_media_init(sc);
254989d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
255089d55360SSepherosa Ziehau 			}
25518892ea20SAggelos Economopoulos 			return;
25528892ea20SAggelos Economopoulos 		}
25538892ea20SAggelos Economopoulos 	}
25547cc92483SSepherosa Ziehau 	if (bootverbose) {
2555af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
25567cc92483SSepherosa Ziehau 		    cmd.data0);
25577cc92483SSepherosa Ziehau 	}
25588892ea20SAggelos Economopoulos }
25598892ea20SAggelos Economopoulos 
25608892ea20SAggelos Economopoulos static void
2561cf5afd69SSepherosa Ziehau mxge_intr_status(struct mxge_softc *sc, const mcp_irq_data_t *stats)
25628892ea20SAggelos Economopoulos {
25638892ea20SAggelos Economopoulos 	if (sc->link_state != stats->link_up) {
25648892ea20SAggelos Economopoulos 		sc->link_state = stats->link_up;
25658892ea20SAggelos Economopoulos 		if (sc->link_state) {
256673a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_UP;
256773a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
25687cc92483SSepherosa Ziehau 			if (bootverbose)
2569cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link up\n");
25708892ea20SAggelos Economopoulos 		} else {
257173a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
257273a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
25737cc92483SSepherosa Ziehau 			if (bootverbose)
2574cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link down\n");
25758892ea20SAggelos Economopoulos 		}
25768892ea20SAggelos Economopoulos 		sc->need_media_probe = 1;
25778892ea20SAggelos Economopoulos 	}
2578cf5afd69SSepherosa Ziehau 
2579cf5afd69SSepherosa Ziehau 	if (sc->rdma_tags_available != be32toh(stats->rdma_tags_available)) {
2580cf5afd69SSepherosa Ziehau 		sc->rdma_tags_available = be32toh(stats->rdma_tags_available);
2581cf5afd69SSepherosa Ziehau 		if_printf(sc->ifp, "RDMA timed out! %d tags left\n",
2582cf5afd69SSepherosa Ziehau 		    sc->rdma_tags_available);
25838892ea20SAggelos Economopoulos 	}
25848892ea20SAggelos Economopoulos 
25858892ea20SAggelos Economopoulos 	if (stats->link_down) {
25868892ea20SAggelos Economopoulos 		sc->down_cnt += stats->link_down;
25878892ea20SAggelos Economopoulos 		sc->link_state = 0;
2588f0115d64SAggelos Economopoulos 		sc->ifp->if_link_state = LINK_STATE_DOWN;
2589f0115d64SAggelos Economopoulos 		if_link_state_change(sc->ifp);
25908892ea20SAggelos Economopoulos 	}
25918892ea20SAggelos Economopoulos }
25928892ea20SAggelos Economopoulos 
2593cf5afd69SSepherosa Ziehau static void
259426634ef8SSepherosa Ziehau mxge_serialize_skipmain(struct mxge_softc *sc)
259526634ef8SSepherosa Ziehau {
259626634ef8SSepherosa Ziehau 	lwkt_serialize_array_enter(sc->serializes, sc->nserialize, 1);
259726634ef8SSepherosa Ziehau }
259826634ef8SSepherosa Ziehau 
259926634ef8SSepherosa Ziehau static void
260026634ef8SSepherosa Ziehau mxge_deserialize_skipmain(struct mxge_softc *sc)
260126634ef8SSepherosa Ziehau {
260226634ef8SSepherosa Ziehau 	lwkt_serialize_array_exit(sc->serializes, sc->nserialize, 1);
260326634ef8SSepherosa Ziehau }
260426634ef8SSepherosa Ziehau 
260526634ef8SSepherosa Ziehau static void
2606cf5afd69SSepherosa Ziehau mxge_legacy(void *arg)
2607cf5afd69SSepherosa Ziehau {
2608cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2609cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2610cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2611cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26129a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2613cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2614cf5afd69SSepherosa Ziehau 	uint8_t valid;
2615cf5afd69SSepherosa Ziehau 
261626634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
261726634ef8SSepherosa Ziehau 
2618cf5afd69SSepherosa Ziehau #if 0
2619cf5afd69SSepherosa Ziehau 	/* an interrupt on a non-zero slice is implicitly valid
2620cf5afd69SSepherosa Ziehau 	   since MSI-X irqs are not shared */
2621cf5afd69SSepherosa Ziehau 	if (ss != sc->ss) {
2622cf5afd69SSepherosa Ziehau 		mxge_clean_rx_done(rx_done);
2623cf5afd69SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2624cf5afd69SSepherosa Ziehau 		return;
2625cf5afd69SSepherosa Ziehau 	}
2626cf5afd69SSepherosa Ziehau #endif
2627cf5afd69SSepherosa Ziehau 
2628cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2629cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2630cf5afd69SSepherosa Ziehau 		return;
2631cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2632cf5afd69SSepherosa Ziehau 
2633cf5afd69SSepherosa Ziehau 	/* Lower legacy IRQ */
2634cf5afd69SSepherosa Ziehau 	*sc->irq_deassert = 0;
2635cf5afd69SSepherosa Ziehau 	if (!mxge_deassert_wait) {
2636cf5afd69SSepherosa Ziehau 		/* Don't wait for conf. that irq is low */
2637cf5afd69SSepherosa Ziehau 		stats->valid = 0;
2638cf5afd69SSepherosa Ziehau 	}
2639cf5afd69SSepherosa Ziehau 
264026634ef8SSepherosa Ziehau 	mxge_serialize_skipmain(sc);
264126634ef8SSepherosa Ziehau 
2642cf5afd69SSepherosa Ziehau 	/*
2643cf5afd69SSepherosa Ziehau 	 * Loop while waiting for legacy irq deassertion
2644cf5afd69SSepherosa Ziehau 	 * XXX do we really want to loop?
2645cf5afd69SSepherosa Ziehau 	 */
2646cf5afd69SSepherosa Ziehau 	do {
2647cf5afd69SSepherosa Ziehau 		/* Check for transmit completes and receives */
2648cf5afd69SSepherosa Ziehau 		send_done_count = be32toh(stats->send_done_count);
2649cf5afd69SSepherosa Ziehau 		while ((send_done_count != tx->pkt_done) ||
2650cf5afd69SSepherosa Ziehau 		       (rx_done->entry[rx_done->idx].length != 0)) {
2651cf5afd69SSepherosa Ziehau 			if (send_done_count != tx->pkt_done)
2652cf5afd69SSepherosa Ziehau 				mxge_tx_done(tx, (int)send_done_count);
2653cf5afd69SSepherosa Ziehau 			mxge_clean_rx_done(rx_done);
2654cf5afd69SSepherosa Ziehau 			send_done_count = be32toh(stats->send_done_count);
2655cf5afd69SSepherosa Ziehau 		}
2656cf5afd69SSepherosa Ziehau 		if (mxge_deassert_wait)
2657cf5afd69SSepherosa Ziehau 			wmb();
2658cf5afd69SSepherosa Ziehau 	} while (*((volatile uint8_t *)&stats->valid));
2659cf5afd69SSepherosa Ziehau 
266026634ef8SSepherosa Ziehau 	mxge_deserialize_skipmain(sc);
266126634ef8SSepherosa Ziehau 
2662cf5afd69SSepherosa Ziehau 	/* Fw link & error stats meaningful only on the first slice */
2663cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2664cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2665cf5afd69SSepherosa Ziehau 
2666cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
2667cf5afd69SSepherosa Ziehau 	if (valid & 0x1)
2668cf5afd69SSepherosa Ziehau 	    *ss->irq_claim = be32toh(3);
2669cf5afd69SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2670cf5afd69SSepherosa Ziehau }
2671cf5afd69SSepherosa Ziehau 
2672cf5afd69SSepherosa Ziehau static void
2673cf5afd69SSepherosa Ziehau mxge_msi(void *arg)
2674cf5afd69SSepherosa Ziehau {
2675cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2676cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2677cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2678cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26799a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2680cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2681cf5afd69SSepherosa Ziehau 	uint8_t valid;
2682cf5afd69SSepherosa Ziehau 
268326634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
268426634ef8SSepherosa Ziehau 
2685cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2686cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2687cf5afd69SSepherosa Ziehau 		return;
2688cf5afd69SSepherosa Ziehau 
2689cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2690cf5afd69SSepherosa Ziehau 	stats->valid = 0;
2691cf5afd69SSepherosa Ziehau 
2692cf5afd69SSepherosa Ziehau 	/* Check for receives */
269326634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&ss->rx_data.rx_serialize);
2694cf5afd69SSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0)
2695cf5afd69SSepherosa Ziehau 		mxge_clean_rx_done(rx_done);
269626634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&ss->rx_data.rx_serialize);
2697cf5afd69SSepherosa Ziehau 
269845bc9b9dSSepherosa Ziehau 	/*
269945bc9b9dSSepherosa Ziehau 	 * Check for transmit completes
270045bc9b9dSSepherosa Ziehau 	 *
270145bc9b9dSSepherosa Ziehau 	 * NOTE:
270245bc9b9dSSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
270345bc9b9dSSepherosa Ziehau 	 * which is called only in interrupt handler, the
270445bc9b9dSSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
270545bc9b9dSSepherosa Ziehau 	 */
2706cf5afd69SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
270745bc9b9dSSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
270845bc9b9dSSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
2709cf5afd69SSepherosa Ziehau 		mxge_tx_done(tx, (int)send_done_count);
271026634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
271145bc9b9dSSepherosa Ziehau 	}
2712cf5afd69SSepherosa Ziehau 
2713cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2714cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2715cf5afd69SSepherosa Ziehau 
2716cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
27178892ea20SAggelos Economopoulos 	if (valid & 0x1)
27188892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
27198892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
27208892ea20SAggelos Economopoulos }
27218892ea20SAggelos Economopoulos 
27228892ea20SAggelos Economopoulos static void
27238892ea20SAggelos Economopoulos mxge_init(void *arg)
27248892ea20SAggelos Economopoulos {
272589d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
272689d55360SSepherosa Ziehau 
272726634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(sc->ifp);
272889d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
272989d55360SSepherosa Ziehau 		mxge_open(sc);
27308892ea20SAggelos Economopoulos }
27318892ea20SAggelos Economopoulos 
27328892ea20SAggelos Economopoulos static void
27338892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
27348892ea20SAggelos Economopoulos {
27358892ea20SAggelos Economopoulos 	int i;
27368892ea20SAggelos Economopoulos 
27379a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
27389a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.info[i].m == NULL)
27398892ea20SAggelos Economopoulos 			continue;
27409a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_big.dmat,
27419a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map);
27429a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_big.info[i].m);
27439a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info[i].m = NULL;
27448892ea20SAggelos Economopoulos 	}
27458892ea20SAggelos Economopoulos 
27469a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
27479a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.info[i].m == NULL)
27488892ea20SAggelos Economopoulos 			continue;
27499a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_small.dmat,
27509a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map);
27519a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_small.info[i].m);
27529a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info[i].m = NULL;
27538892ea20SAggelos Economopoulos 	}
27548892ea20SAggelos Economopoulos 
27554e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
27568892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
27578892ea20SAggelos Economopoulos 		return;
27588892ea20SAggelos Economopoulos 
27598892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
27608892ea20SAggelos Economopoulos 		ss->tx.info[i].flag = 0;
27618892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
27628892ea20SAggelos Economopoulos 			continue;
27634e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
27648892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
27658892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
27668892ea20SAggelos Economopoulos 	}
27678892ea20SAggelos Economopoulos }
27688892ea20SAggelos Economopoulos 
27698892ea20SAggelos Economopoulos static void
27708892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
27718892ea20SAggelos Economopoulos {
27728892ea20SAggelos Economopoulos 	int slice;
27738892ea20SAggelos Economopoulos 
27748892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
27758892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
27768892ea20SAggelos Economopoulos }
27778892ea20SAggelos Economopoulos 
27788892ea20SAggelos Economopoulos static void
27798892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
27808892ea20SAggelos Economopoulos {
27818892ea20SAggelos Economopoulos 	int i;
27828892ea20SAggelos Economopoulos 
27839a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_done.entry != NULL) {
27849a4ae890SSepherosa Ziehau 		mxge_dma_free(&ss->rx_data.rx_done.dma);
27859a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = NULL;
2786798c3369SSepherosa Ziehau 	}
27878892ea20SAggelos Economopoulos 
278811868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
278911868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
279011868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2791798c3369SSepherosa Ziehau 	}
27928892ea20SAggelos Economopoulos 
2793798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2794d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
27958892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2796798c3369SSepherosa Ziehau 	}
27978892ea20SAggelos Economopoulos 
27989a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.shadow != NULL) {
27999a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.shadow, M_DEVBUF);
28009a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.shadow = NULL;
2801798c3369SSepherosa Ziehau 	}
28028892ea20SAggelos Economopoulos 
28039a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.shadow != NULL) {
28049a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.shadow, M_DEVBUF);
28059a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow = NULL;
2806798c3369SSepherosa Ziehau 	}
28078892ea20SAggelos Economopoulos 
28088892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
28098892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
28108892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
28118892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
28128892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
28138892ea20SAggelos Economopoulos 			}
28148892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
28158892ea20SAggelos Economopoulos 		}
2816d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
28178892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2818798c3369SSepherosa Ziehau 	}
28198892ea20SAggelos Economopoulos 
28209a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.info != NULL) {
28219a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.dmat != NULL) {
28229a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
28239a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
28249a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[i].map);
28258892ea20SAggelos Economopoulos 			}
28269a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
28279a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
28289a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
28298892ea20SAggelos Economopoulos 		}
28309a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.info, M_DEVBUF);
28319a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info = NULL;
2832798c3369SSepherosa Ziehau 	}
28338892ea20SAggelos Economopoulos 
28349a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.info != NULL) {
28359a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.dmat != NULL) {
28369a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
28379a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
28389a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[i].map);
28398892ea20SAggelos Economopoulos 			}
28409a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
28419a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
28429a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
28438892ea20SAggelos Economopoulos 		}
28449a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.info, M_DEVBUF);
28459a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info = NULL;
28468892ea20SAggelos Economopoulos 	}
2847798c3369SSepherosa Ziehau }
28488892ea20SAggelos Economopoulos 
28498892ea20SAggelos Economopoulos static void
28508892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
28518892ea20SAggelos Economopoulos {
28528892ea20SAggelos Economopoulos 	int slice;
28538892ea20SAggelos Economopoulos 
2854798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2855798c3369SSepherosa Ziehau 		return;
2856798c3369SSepherosa Ziehau 
28578892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
28588892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
28598892ea20SAggelos Economopoulos }
28608892ea20SAggelos Economopoulos 
28618892ea20SAggelos Economopoulos static int
28628892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28638892ea20SAggelos Economopoulos     int tx_ring_entries)
28648892ea20SAggelos Economopoulos {
28658892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
28668892ea20SAggelos Economopoulos 	size_t bytes;
28678892ea20SAggelos Economopoulos 	int err, i;
28688892ea20SAggelos Economopoulos 
28697cc92483SSepherosa Ziehau 	/*
28707cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
28717cc92483SSepherosa Ziehau 	 */
28728892ea20SAggelos Economopoulos 
28739a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.mask = ss->rx_data.rx_big.mask =
28749a4ae890SSepherosa Ziehau 	    rx_ring_entries - 1;
28759a4ae890SSepherosa Ziehau 	ss->rx_data.rx_done.mask = (2 * rx_ring_entries) - 1;
28768892ea20SAggelos Economopoulos 
28777cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
28789a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.shadow);
28799a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28808892ea20SAggelos Economopoulos 
28819a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.shadow);
28829a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28838892ea20SAggelos Economopoulos 
28847cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
28859a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.info);
28869a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28878892ea20SAggelos Economopoulos 
28889a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.info);
28899a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28908892ea20SAggelos Economopoulos 
28917cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
28928892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28938892ea20SAggelos Economopoulos 				 1,			/* alignment */
28948892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28958892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28968892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28978892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
28988892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
28998892ea20SAggelos Economopoulos 				 1,			/* num segs */
29008892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
29017cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
29027cc92483SSepherosa Ziehau 				 			/* flags */
29039a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_small.dmat); /* tag */
29048892ea20SAggelos Economopoulos 	if (err != 0) {
29058892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
29068892ea20SAggelos Economopoulos 		    err);
29073598cc14SSascha Wildner 		return err;
29088892ea20SAggelos Economopoulos 	}
29098892ea20SAggelos Economopoulos 
29109a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_small.dmat, BUS_DMA_WAITOK,
29119a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_small.extra_map);
2912798c3369SSepherosa Ziehau 	if (err != 0) {
2913798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
29149a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29159a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.dmat = NULL;
2916798c3369SSepherosa Ziehau 		return err;
2917798c3369SSepherosa Ziehau 	}
29189a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
29199a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_small.dmat,
29209a4ae890SSepherosa Ziehau 		    BUS_DMA_WAITOK, &ss->rx_data.rx_small.info[i].map);
2921798c3369SSepherosa Ziehau 		if (err != 0) {
2922798c3369SSepherosa Ziehau 			int j;
2923798c3369SSepherosa Ziehau 
2924798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
2925798c3369SSepherosa Ziehau 
2926798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
29279a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29289a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[j].map);
2929798c3369SSepherosa Ziehau 			}
29309a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29319a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
29329a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29339a4ae890SSepherosa Ziehau 			ss->rx_data.rx_small.dmat = NULL;
2934798c3369SSepherosa Ziehau 			return err;
2935798c3369SSepherosa Ziehau 		}
2936798c3369SSepherosa Ziehau 	}
2937798c3369SSepherosa Ziehau 
29388892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29398892ea20SAggelos Economopoulos 				 1,			/* alignment */
29408892ea20SAggelos Economopoulos 				 4096,			/* boundary */
29418892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29428892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29438892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2944b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
2945b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
29468892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
29477cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
29487cc92483SSepherosa Ziehau 				 			/* flags */
29499a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_big.dmat); /* tag */
29508892ea20SAggelos Economopoulos 	if (err != 0) {
29518892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
29528892ea20SAggelos Economopoulos 		    err);
29533598cc14SSascha Wildner 		return err;
29548892ea20SAggelos Economopoulos 	}
29557cc92483SSepherosa Ziehau 
29569a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
29579a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_big.extra_map);
29588892ea20SAggelos Economopoulos 	if (err != 0) {
29597cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
29609a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29619a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.dmat = NULL;
29623598cc14SSascha Wildner 		return err;
29638892ea20SAggelos Economopoulos 	}
29649a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
29659a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
29669a4ae890SSepherosa Ziehau 		    &ss->rx_data.rx_big.info[i].map);
2967798c3369SSepherosa Ziehau 		if (err != 0) {
2968798c3369SSepherosa Ziehau 			int j;
2969798c3369SSepherosa Ziehau 
2970798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
2971798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
29729a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29739a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[j].map);
2974798c3369SSepherosa Ziehau 			}
29759a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29769a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
29779a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29789a4ae890SSepherosa Ziehau 			ss->rx_data.rx_big.dmat = NULL;
2979798c3369SSepherosa Ziehau 			return err;
2980798c3369SSepherosa Ziehau 		}
2981798c3369SSepherosa Ziehau 	}
29828892ea20SAggelos Economopoulos 
29837cc92483SSepherosa Ziehau 	/*
29847cc92483SSepherosa Ziehau 	 * Now allocate TX resources
29857cc92483SSepherosa Ziehau 	 */
29868892ea20SAggelos Economopoulos 
29878892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
29888892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
29898892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
29908892ea20SAggelos Economopoulos 		return 0;
29918892ea20SAggelos Economopoulos #endif
29928892ea20SAggelos Economopoulos 
29938892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
29948892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
29958892ea20SAggelos Economopoulos 
299611868a93SSepherosa Ziehau 	/* Allocate the tx request copy block; MUST be 8 bytes aligned */
299711868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
299811868a93SSepherosa Ziehau 	ss->tx.req_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
299911868a93SSepherosa Ziehau 	/* DragonFly's kmalloc(9) promises at least 8 bytes alignment */
300011868a93SSepherosa Ziehau 	KASSERT(((uintptr_t)ss->tx.req_list & 0x7) == 0,
300111868a93SSepherosa Ziehau 	    ("req_list not 8 bytes aligned"));
30028892ea20SAggelos Economopoulos 
30037cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
30048892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
30057cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
30068892ea20SAggelos Economopoulos 
30077cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
30088892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
3009d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30108892ea20SAggelos Economopoulos 
30117cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
30128892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30138892ea20SAggelos Economopoulos 				 1,			/* alignment */
30148892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
30158892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30168892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30178892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
30187cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
30197cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
30207cc92483SSepherosa Ziehau 				 			/* maxsize */
30218892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
30228892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
30237cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
30247cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
30258892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
30268892ea20SAggelos Economopoulos 	if (err != 0) {
30277cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
30283598cc14SSascha Wildner 		return err;
30298892ea20SAggelos Economopoulos 	}
30308892ea20SAggelos Economopoulos 
30317cc92483SSepherosa Ziehau 	/*
30327cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
30337cc92483SSepherosa Ziehau 	 */
30348892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
30357cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
30367cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
30378892ea20SAggelos Economopoulos 		if (err != 0) {
3038798c3369SSepherosa Ziehau 			int j;
3039798c3369SSepherosa Ziehau 
30407cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
3041798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
3042798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
3043798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
3044798c3369SSepherosa Ziehau 			}
3045798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
3046798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
30473598cc14SSascha Wildner 			return err;
30488892ea20SAggelos Economopoulos 		}
30498892ea20SAggelos Economopoulos 	}
30508892ea20SAggelos Economopoulos 	return 0;
30518892ea20SAggelos Economopoulos }
30528892ea20SAggelos Economopoulos 
30538892ea20SAggelos Economopoulos static int
30548892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
30558892ea20SAggelos Economopoulos {
30568892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30578892ea20SAggelos Economopoulos 	int tx_ring_size;
30588892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
30598892ea20SAggelos Economopoulos 	int err, slice;
30608892ea20SAggelos Economopoulos 
30617cc92483SSepherosa Ziehau 	/* Get ring sizes */
30628892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30638892ea20SAggelos Economopoulos 	if (err != 0) {
30648892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3065798c3369SSepherosa Ziehau 		return err;
30668892ea20SAggelos Economopoulos 	}
30677cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
30688892ea20SAggelos Economopoulos 
30698892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
30708892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof(mcp_dma_addr_t);
3071f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3072f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
30738892ea20SAggelos Economopoulos 
30748892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
30758892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30767cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3077798c3369SSepherosa Ziehau 		if (err != 0) {
3078798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3079798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3080798c3369SSepherosa Ziehau 			return err;
3081798c3369SSepherosa Ziehau 		}
30828892ea20SAggelos Economopoulos 	}
30838892ea20SAggelos Economopoulos 	return 0;
30848892ea20SAggelos Economopoulos }
30858892ea20SAggelos Economopoulos 
30868892ea20SAggelos Economopoulos static void
3087b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
30888892ea20SAggelos Economopoulos {
3089b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
30908892ea20SAggelos Economopoulos 
30918892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
30928892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3093b9a8961fSSepherosa Ziehau 	} else {
3094b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
30958892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
30968892ea20SAggelos Economopoulos 	}
30978892ea20SAggelos Economopoulos }
30988892ea20SAggelos Economopoulos 
30998892ea20SAggelos Economopoulos static int
3100b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
31018892ea20SAggelos Economopoulos {
31028892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
31038892ea20SAggelos Economopoulos 	int err, i, slice;
31048892ea20SAggelos Economopoulos 
3105308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
31068892ea20SAggelos Economopoulos 
3107308781adSSepherosa Ziehau 	/*
3108308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3109308781adSSepherosa Ziehau 	 */
31108892ea20SAggelos Economopoulos 	err = 0;
31118892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
31128892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
31138892ea20SAggelos Economopoulos 	if (slice == 0) {
31148892ea20SAggelos Economopoulos #endif
31158892ea20SAggelos Economopoulos 		cmd.data0 = slice;
3116308781adSSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3117308781adSSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3118308781adSSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
31198892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
3120308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
31218892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
3122308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
31238892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
31248892ea20SAggelos Economopoulos 	}
31258892ea20SAggelos Economopoulos #endif
3126308781adSSepherosa Ziehau 
31278892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3128308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
31299a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.lanai =
3130308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3131308781adSSepherosa Ziehau 
31328892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3133308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
31349a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.lanai =
3135308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
31368892ea20SAggelos Economopoulos 
31378892ea20SAggelos Economopoulos 	if (err != 0) {
3138308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
31398892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
31408892ea20SAggelos Economopoulos 		return EIO;
31418892ea20SAggelos Economopoulos 	}
31428892ea20SAggelos Economopoulos 
3143308781adSSepherosa Ziehau 	/*
3144308781adSSepherosa Ziehau 	 * Stock small receive ring
3145308781adSSepherosa Ziehau 	 */
31469a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
31479a4ae890SSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_data.rx_small,
31489a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map, i, TRUE);
31498892ea20SAggelos Economopoulos 		if (err) {
3150308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
31519a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.mask + 1);
31528892ea20SAggelos Economopoulos 			return ENOMEM;
31538892ea20SAggelos Economopoulos 		}
31548892ea20SAggelos Economopoulos 	}
3155308781adSSepherosa Ziehau 
3156308781adSSepherosa Ziehau 	/*
3157308781adSSepherosa Ziehau 	 * Stock big receive ring
3158308781adSSepherosa Ziehau 	 */
31599a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
31609a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_low = 0xffffffff;
31619a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_high = 0xffffffff;
31628892ea20SAggelos Economopoulos 	}
3163308781adSSepherosa Ziehau 
31649a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.cl_size = cl_size;
31659a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
3166b915556eSAggelos Economopoulos 	    EVL_ENCAPLEN + MXGEFW_PAD;
3167308781adSSepherosa Ziehau 
31689a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
31699a4ae890SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_data.rx_big,
31709a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map, i, TRUE);
31718892ea20SAggelos Economopoulos 		if (err) {
3172308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
31739a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.mask + 1);
31748892ea20SAggelos Economopoulos 			return ENOMEM;
31758892ea20SAggelos Economopoulos 		}
31768892ea20SAggelos Economopoulos 	}
31778892ea20SAggelos Economopoulos 	return 0;
31788892ea20SAggelos Economopoulos }
31798892ea20SAggelos Economopoulos 
31808892ea20SAggelos Economopoulos static int
31818892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
31828892ea20SAggelos Economopoulos {
31836ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
31848892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3185b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
31868892ea20SAggelos Economopoulos 	bus_addr_t bus;
31878892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
31888892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
31898892ea20SAggelos Economopoulos 
319026634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
31916ee6cba3SSepherosa Ziehau 
31928892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
31936ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
31948892ea20SAggelos Economopoulos 
31958892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
31968892ea20SAggelos Economopoulos 	if (err != 0) {
31976ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
31988892ea20SAggelos Economopoulos 		return EIO;
31998892ea20SAggelos Economopoulos 	}
32008892ea20SAggelos Economopoulos 
32018892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
32026ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
32038892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
32046ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
32058892ea20SAggelos Economopoulos 
32066ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
32078892ea20SAggelos Economopoulos 		if (err != 0) {
32086ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
32098892ea20SAggelos Economopoulos 			return err;
32108892ea20SAggelos Economopoulos 		}
32118892ea20SAggelos Economopoulos 
32126ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
32138892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
32148892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
32158892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
32168892ea20SAggelos Economopoulos 
32178892ea20SAggelos Economopoulos 		cmd.data0 = 1;
32186ee6cba3SSepherosa Ziehau 		cmd.data1 = MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
32198892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
32208892ea20SAggelos Economopoulos 		if (err != 0) {
32216ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
32228892ea20SAggelos Economopoulos 			return err;
32238892ea20SAggelos Economopoulos 		}
32248892ea20SAggelos Economopoulos 	}
32258892ea20SAggelos Economopoulos 
322689d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
322789d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
322889d55360SSepherosa Ziehau 	if (err) {
32296ee6cba3SSepherosa Ziehau 		/*
32306ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
32316ee6cba3SSepherosa Ziehau 		 */
32326ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
32336ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
32346ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
32356ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
323689d55360SSepherosa Ziehau 	}
32378892ea20SAggelos Economopoulos 
3238b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
32398892ea20SAggelos Economopoulos 
3240b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
32416ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
32426ee6cba3SSepherosa Ziehau 	/*
32436ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
32446ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
32456ee6cba3SSepherosa Ziehau 	 */
32466ee6cba3SSepherosa Ziehau 
32476ee6cba3SSepherosa Ziehau 	/*
32486ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
32496ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
32502f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
32516ee6cba3SSepherosa Ziehau 	 */
32526ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
32538892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
32546ee6cba3SSepherosa Ziehau 
3255b9a8961fSSepherosa Ziehau 	/* XXX need to cut MXGEFW_PAD here? */
32568892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
32576ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
32586ee6cba3SSepherosa Ziehau 
3259b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
32608892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32618892ea20SAggelos Economopoulos 
32628892ea20SAggelos Economopoulos 	if (err != 0) {
32636ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32648892ea20SAggelos Economopoulos 		goto abort;
32658892ea20SAggelos Economopoulos 	}
32668892ea20SAggelos Economopoulos 
32678892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
32688892ea20SAggelos Economopoulos 	for (slice = 0;
32698892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
32708892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
32718892ea20SAggelos Economopoulos #else
32728892ea20SAggelos Economopoulos 	     slice < 1;
32738892ea20SAggelos Economopoulos #endif
32748892ea20SAggelos Economopoulos 	     slice++) {
32758892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
32767cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32777cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32788892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
32798892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
32808892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32818892ea20SAggelos Economopoulos 	}
32828892ea20SAggelos Economopoulos 
32838892ea20SAggelos Economopoulos 	if (err != 0) {
32847cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
32858892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
32868892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
32878892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
32886ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
32898892ea20SAggelos Economopoulos 		    &cmd);
32906ee6cba3SSepherosa Ziehau 
32918892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
32928892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
32938892ea20SAggelos Economopoulos 	} else {
32948892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
32958892ea20SAggelos Economopoulos 	}
32968892ea20SAggelos Economopoulos 
32978892ea20SAggelos Economopoulos 	if (err != 0) {
32986ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32998892ea20SAggelos Economopoulos 		goto abort;
33008892ea20SAggelos Economopoulos 	}
33018892ea20SAggelos Economopoulos 
33028892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3303b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
33048892ea20SAggelos Economopoulos 		if (err != 0) {
33056ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
33068892ea20SAggelos Economopoulos 			goto abort;
33078892ea20SAggelos Economopoulos 		}
33088892ea20SAggelos Economopoulos 	}
33098892ea20SAggelos Economopoulos 
33108892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
33118892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
33128892ea20SAggelos Economopoulos 	if (err) {
33136ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
33148892ea20SAggelos Economopoulos 		goto abort;
33158892ea20SAggelos Economopoulos 	}
33166ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
33176ee6cba3SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3318ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
33198892ea20SAggelos Economopoulos 
33208892ea20SAggelos Economopoulos 	return 0;
33218892ea20SAggelos Economopoulos 
33228892ea20SAggelos Economopoulos abort:
33238892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33248892ea20SAggelos Economopoulos 	return err;
33258892ea20SAggelos Economopoulos }
33268892ea20SAggelos Economopoulos 
33272c29ffc6SSepherosa Ziehau static void
332889d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
33298892ea20SAggelos Economopoulos {
33302c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
33318892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
33328892ea20SAggelos Economopoulos 	int err, old_down_cnt;
33338892ea20SAggelos Economopoulos 
333426634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
333589d55360SSepherosa Ziehau 
33362c29ffc6SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
33372c29ffc6SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3338ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
33392c29ffc6SSepherosa Ziehau 
334089d55360SSepherosa Ziehau 	if (!down) {
33418892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
33428892ea20SAggelos Economopoulos 		wmb();
33432c29ffc6SSepherosa Ziehau 
33448892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
33452c29ffc6SSepherosa Ziehau 		if (err)
33462c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
33472c29ffc6SSepherosa Ziehau 
33488892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
33492c29ffc6SSepherosa Ziehau 			/* Wait for down irq */
335026634ef8SSepherosa Ziehau 			ifnet_deserialize_all(ifp);
33518892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
335226634ef8SSepherosa Ziehau 			ifnet_serialize_all(ifp);
33538892ea20SAggelos Economopoulos 		}
33542c29ffc6SSepherosa Ziehau 
33558892ea20SAggelos Economopoulos 		wmb();
33562c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
33572c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
335889d55360SSepherosa Ziehau 	}
33598892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33608892ea20SAggelos Economopoulos }
33618892ea20SAggelos Economopoulos 
33628892ea20SAggelos Economopoulos static void
33638892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
33648892ea20SAggelos Economopoulos {
33658892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33668892ea20SAggelos Economopoulos 	int reg;
336789d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
33688892ea20SAggelos Economopoulos 
33697cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
33708892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
33718892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
33728892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
33738892ea20SAggelos Economopoulos 
337489d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
33758892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
33768892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
33778892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
337889d55360SSepherosa Ziehau 			sc->pectl = pectl;
337989d55360SSepherosa Ziehau 		} else {
33807cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
338189d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
338289d55360SSepherosa Ziehau 		}
33838892ea20SAggelos Economopoulos 	}
33848892ea20SAggelos Economopoulos 
33857cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
33868892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
33878892ea20SAggelos Economopoulos }
33888892ea20SAggelos Economopoulos 
33898892ea20SAggelos Economopoulos static uint32_t
33908892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
33918892ea20SAggelos Economopoulos {
33928892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33938892ea20SAggelos Economopoulos 	uint32_t vs;
33948892ea20SAggelos Economopoulos 
33958a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
33968892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
33978a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
33988892ea20SAggelos Economopoulos 		return (uint32_t)-1;
33998892ea20SAggelos Economopoulos 	}
34008a20b038SSepherosa Ziehau 	/* Enable read32 mode */
34018892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
34028a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
34038892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
34048a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
34058892ea20SAggelos Economopoulos }
34068892ea20SAggelos Economopoulos 
340789d55360SSepherosa Ziehau static void
340889d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
34098892ea20SAggelos Economopoulos {
34108892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
341189d55360SSepherosa Ziehau 	int err, running;
34128892ea20SAggelos Economopoulos 	uint32_t reboot;
34138892ea20SAggelos Economopoulos 	uint16_t cmd;
34148892ea20SAggelos Economopoulos 
34158892ea20SAggelos Economopoulos 	err = ENXIO;
34168892ea20SAggelos Economopoulos 
34178a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
34188892ea20SAggelos Economopoulos 
34198892ea20SAggelos Economopoulos 	/*
34208a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
34218892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
34228892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
34238892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
34248892ea20SAggelos Economopoulos 	 * again
34258892ea20SAggelos Economopoulos 	 */
34268892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
34278892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
34288892ea20SAggelos Economopoulos 		/*
34298a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
34308892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
34318892ea20SAggelos Economopoulos 		 * back, then give up
34328892ea20SAggelos Economopoulos 		 */
34338892ea20SAggelos Economopoulos 		DELAY(1000*100);
34348892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
34358a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
34368a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
34378892ea20SAggelos Economopoulos 	}
34388892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
34398a20b038SSepherosa Ziehau 		/* Print the reboot status */
34408892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
34418a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
34428a20b038SSepherosa Ziehau 
344389d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
344489d55360SSepherosa Ziehau 		if (running) {
344589d55360SSepherosa Ziehau 			/*
34468a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
344789d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
344889d55360SSepherosa Ziehau 			 */
344989d55360SSepherosa Ziehau 
345089d55360SSepherosa Ziehau 			/* Mark the link as down */
345189d55360SSepherosa Ziehau 			if (sc->link_state) {
345289d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
345389d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
345489d55360SSepherosa Ziehau 			}
345589d55360SSepherosa Ziehau 			mxge_close(sc, 1);
345689d55360SSepherosa Ziehau 		}
34578a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
34588892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
34598892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
34608892ea20SAggelos Economopoulos 
34618a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
34628892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
34638892ea20SAggelos Economopoulos 
34648a20b038SSepherosa Ziehau 		/* Reload f/w */
346589d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
34668a20b038SSepherosa Ziehau 		if (err)
34678a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
34688a20b038SSepherosa Ziehau 		if (running && !err) {
346989d55360SSepherosa Ziehau 			err = mxge_open(sc);
347089d55360SSepherosa Ziehau 			if_devstart_sched(sc->ifp);
347189d55360SSepherosa Ziehau 		}
347289d55360SSepherosa Ziehau 		sc->watchdog_resets++;
347389d55360SSepherosa Ziehau 	} else {
34748a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
347589d55360SSepherosa Ziehau 		err = 0;
347689d55360SSepherosa Ziehau 	}
347789d55360SSepherosa Ziehau 	if (err) {
34788a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
347989d55360SSepherosa Ziehau 	} else {
348089d55360SSepherosa Ziehau 		if (sc->dying == 2)
348189d55360SSepherosa Ziehau 			sc->dying = 0;
348289d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
348389d55360SSepherosa Ziehau 	}
348489d55360SSepherosa Ziehau }
348589d55360SSepherosa Ziehau 
348689d55360SSepherosa Ziehau static void
348789d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
348889d55360SSepherosa Ziehau {
34898a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
34908a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
34918892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
34928a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
34938892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
34948a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
34958a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
34968892ea20SAggelos Economopoulos }
34978892ea20SAggelos Economopoulos 
349889d55360SSepherosa Ziehau static u_long
34998892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
35008892ea20SAggelos Economopoulos {
3501cc9c62a4SSepherosa Ziehau 	u_long ipackets, opackets, pkts;
35028892ea20SAggelos Economopoulos 
3503cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, ipackets);
3504cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, opackets);
350589d55360SSepherosa Ziehau 
3506cc9c62a4SSepherosa Ziehau 	pkts = ipackets - sc->ipackets;
3507cc9c62a4SSepherosa Ziehau 	pkts += opackets - sc->opackets;
350889d55360SSepherosa Ziehau 
3509cc9c62a4SSepherosa Ziehau 	sc->ipackets = ipackets;
3510cc9c62a4SSepherosa Ziehau 	sc->opackets = opackets;
3511cc9c62a4SSepherosa Ziehau 
351289d55360SSepherosa Ziehau 	return pkts;
35138892ea20SAggelos Economopoulos }
35148892ea20SAggelos Economopoulos 
35158892ea20SAggelos Economopoulos static void
35168892ea20SAggelos Economopoulos mxge_tick(void *arg)
35178892ea20SAggelos Economopoulos {
35188892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
351989d55360SSepherosa Ziehau 	u_long pkts = 0;
35208892ea20SAggelos Economopoulos 	int err = 0;
3521ca8ca004SSepherosa Ziehau 	int ticks;
35228892ea20SAggelos Economopoulos 
352326634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&sc->main_serialize);
352489d55360SSepherosa Ziehau 
352589d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3526ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3527ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
352889d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3529ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3530ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
353189d55360SSepherosa Ziehau 	}
353289d55360SSepherosa Ziehau 	if (pkts == 0) {
3533cc9c62a4SSepherosa Ziehau 		uint16_t cmd;
3534cc9c62a4SSepherosa Ziehau 
3535ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
353689d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
353789d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
353889d55360SSepherosa Ziehau 			sc->dying = 2;
353926634ef8SSepherosa Ziehau 			mxge_serialize_skipmain(sc);
354089d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
354126634ef8SSepherosa Ziehau 			mxge_deserialize_skipmain(sc);
354289d55360SSepherosa Ziehau 			err = ENXIO;
354389d55360SSepherosa Ziehau 		}
3544cc9c62a4SSepherosa Ziehau 
3545ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
354689d55360SSepherosa Ziehau 		ticks *= 4;
354789d55360SSepherosa Ziehau 	}
354889d55360SSepherosa Ziehau 
35498892ea20SAggelos Economopoulos 	if (err == 0)
355089d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
355189d55360SSepherosa Ziehau 
355226634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&sc->main_serialize);
35538892ea20SAggelos Economopoulos }
35548892ea20SAggelos Economopoulos 
35558892ea20SAggelos Economopoulos static int
35568892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
35578892ea20SAggelos Economopoulos {
35588892ea20SAggelos Economopoulos 	return EINVAL;
35598892ea20SAggelos Economopoulos }
35608892ea20SAggelos Economopoulos 
35618892ea20SAggelos Economopoulos static int
35628892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
35638892ea20SAggelos Economopoulos {
35648892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
35658892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
35668892ea20SAggelos Economopoulos 	int err = 0;
35678892ea20SAggelos Economopoulos 
3568b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3569b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
35708892ea20SAggelos Economopoulos 		return EINVAL;
3571b9a8961fSSepherosa Ziehau 
35728892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
35738892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
35742ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
357589d55360SSepherosa Ziehau 		mxge_close(sc, 0);
35768892ea20SAggelos Economopoulos 		err = mxge_open(sc);
35778892ea20SAggelos Economopoulos 		if (err != 0) {
35788892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
357989d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3580b9a8961fSSepherosa Ziehau 			mxge_open(sc);
35818892ea20SAggelos Economopoulos 		}
35828892ea20SAggelos Economopoulos 	}
35838892ea20SAggelos Economopoulos 	return err;
35848892ea20SAggelos Economopoulos }
35858892ea20SAggelos Economopoulos 
35868892ea20SAggelos Economopoulos static void
35878892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
35888892ea20SAggelos Economopoulos {
35898892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
35908892ea20SAggelos Economopoulos 
35918892ea20SAggelos Economopoulos 
35928892ea20SAggelos Economopoulos 	if (sc == NULL)
35938892ea20SAggelos Economopoulos 		return;
35948892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
359589d55360SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
35968892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
359789d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
35988892ea20SAggelos Economopoulos }
35998892ea20SAggelos Economopoulos 
36008892ea20SAggelos Economopoulos static int
360189d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
360289d55360SSepherosa Ziehau     struct ucred *cr __unused)
36038892ea20SAggelos Economopoulos {
36048892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
36058892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
36068892ea20SAggelos Economopoulos 	int err, mask;
36078892ea20SAggelos Economopoulos 
360826634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
3609af85d4d5SSepherosa Ziehau 	err = 0;
3610af85d4d5SSepherosa Ziehau 
36118892ea20SAggelos Economopoulos 	switch (command) {
36128892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
36138892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
36148892ea20SAggelos Economopoulos 		break;
36158892ea20SAggelos Economopoulos 
36168892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3617af85d4d5SSepherosa Ziehau 		if (sc->dying)
36188892ea20SAggelos Economopoulos 			return EINVAL;
3619af85d4d5SSepherosa Ziehau 
36208892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
36212ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
36228892ea20SAggelos Economopoulos 				err = mxge_open(sc);
36238892ea20SAggelos Economopoulos 			} else {
3624af85d4d5SSepherosa Ziehau 				/*
3625af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3626af85d4d5SSepherosa Ziehau 				 * flag changes
3627af85d4d5SSepherosa Ziehau 				 */
36288892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
36298892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
36308892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
36318892ea20SAggelos Economopoulos 			}
36328892ea20SAggelos Economopoulos 		} else {
3633af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
363489d55360SSepherosa Ziehau 				mxge_close(sc, 0);
36358892ea20SAggelos Economopoulos 		}
36368892ea20SAggelos Economopoulos 		break;
36378892ea20SAggelos Economopoulos 
36388892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
36398892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
36408892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
36418892ea20SAggelos Economopoulos 		break;
36428892ea20SAggelos Economopoulos 
36438892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
36448892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
36458892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
364689d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
364789d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
364889d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
36498892ea20SAggelos Economopoulos 			else
365089d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
36518892ea20SAggelos Economopoulos 		}
365289d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
365389d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
365489d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
365589d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
365689d55360SSepherosa Ziehau 			else
365789d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
365889d55360SSepherosa Ziehau 		}
365989d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
366089d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
36618892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
36628892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
36638892ea20SAggelos Economopoulos 		break;
36648892ea20SAggelos Economopoulos 
36658892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
366689d55360SSepherosa Ziehau 		mxge_media_probe(sc);
36678892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
36688892ea20SAggelos Economopoulos 		    &sc->media, command);
36698892ea20SAggelos Economopoulos 		break;
36708892ea20SAggelos Economopoulos 
36718892ea20SAggelos Economopoulos 	default:
367289d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
367389d55360SSepherosa Ziehau 		break;
36748892ea20SAggelos Economopoulos 	}
36758892ea20SAggelos Economopoulos 	return err;
36768892ea20SAggelos Economopoulos }
36778892ea20SAggelos Economopoulos 
36788892ea20SAggelos Economopoulos static void
36798892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
36808892ea20SAggelos Economopoulos {
36817cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
36827cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
36837cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
36848892ea20SAggelos Economopoulos 
36857cc92483SSepherosa Ziehau 	/* XXX */
36868892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
36878892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
36887cc92483SSepherosa Ziehau 
36898892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
36908892ea20SAggelos Economopoulos 
369189d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
36927cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
36937cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
36947cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
36957cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
369689d55360SSepherosa Ziehau }
36978892ea20SAggelos Economopoulos 
36988892ea20SAggelos Economopoulos static void
36998892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
37008892ea20SAggelos Economopoulos {
37018892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37028892ea20SAggelos Economopoulos 	int i;
37038892ea20SAggelos Economopoulos 
37048892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
37058892ea20SAggelos Economopoulos 		return;
37068892ea20SAggelos Economopoulos 
37078892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37088892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37098892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
37108892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
37118892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
37128892ea20SAggelos Economopoulos 		}
37139a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_done.entry != NULL) {
37149a4ae890SSepherosa Ziehau 			mxge_dma_free(&ss->rx_data.rx_done.dma);
37159a4ae890SSepherosa Ziehau 			ss->rx_data.rx_done.entry = NULL;
37168892ea20SAggelos Economopoulos 		}
37178892ea20SAggelos Economopoulos 	}
37186c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
37198892ea20SAggelos Economopoulos 	sc->ss = NULL;
37208892ea20SAggelos Economopoulos }
37218892ea20SAggelos Economopoulos 
37228892ea20SAggelos Economopoulos static int
37238892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
37248892ea20SAggelos Economopoulos {
37258892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
37268892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37278892ea20SAggelos Economopoulos 	size_t bytes;
37288892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
37298892ea20SAggelos Economopoulos 
37308892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37318892ea20SAggelos Economopoulos 	if (err != 0) {
37328892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37338892ea20SAggelos Economopoulos 		return err;
37348892ea20SAggelos Economopoulos 	}
37358892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
37368892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37378892ea20SAggelos Economopoulos 
37388892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
37397cc92483SSepherosa Ziehau 	sc->ss = kmalloc(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
37407cc92483SSepherosa Ziehau 
37418892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37428892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37438892ea20SAggelos Economopoulos 
37448892ea20SAggelos Economopoulos 		ss->sc = sc;
3745cc9c62a4SSepherosa Ziehau 		ss->tx.sc = sc;
37469a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.sc = sc;
37479a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.sc = sc;
37489a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.rx_big = &ss->rx_data.rx_big;
37499a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.rx_small = &ss->rx_data.rx_small;
37508892ea20SAggelos Economopoulos 
375126634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->rx_data.rx_serialize);
375226634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->tx.tx_serialize);
375326634ef8SSepherosa Ziehau 
37547cc92483SSepherosa Ziehau 		/*
37557cc92483SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queues
37567cc92483SSepherosa Ziehau 		 */
37579a4ae890SSepherosa Ziehau 		bytes = max_intr_slots * sizeof(*ss->rx_data.rx_done.entry);
37589a4ae890SSepherosa Ziehau 		err = mxge_dma_alloc(sc, &ss->rx_data.rx_done.dma, bytes, 4096);
3759798c3369SSepherosa Ziehau 		if (err != 0) {
3760798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3761798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3762798c3369SSepherosa Ziehau 			return err;
3763798c3369SSepherosa Ziehau 		}
37649a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = ss->rx_data.rx_done.dma.dmem_addr;
37658892ea20SAggelos Economopoulos 
37668892ea20SAggelos Economopoulos 		/*
37677cc92483SSepherosa Ziehau 		 * Allocate the per-slice firmware stats; stats
37688892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
37698892ea20SAggelos Economopoulos 		 * slice for now
37708892ea20SAggelos Economopoulos 		 */
37718892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
37728892ea20SAggelos Economopoulos 		if (i > 0)
37738892ea20SAggelos Economopoulos 			continue;
37748892ea20SAggelos Economopoulos #endif
37758892ea20SAggelos Economopoulos 
37768892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
37778892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37788892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3779798c3369SSepherosa Ziehau 		if (err != 0) {
3780798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3781798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3782798c3369SSepherosa Ziehau 			return err;
3783798c3369SSepherosa Ziehau 		}
37847cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
37858892ea20SAggelos Economopoulos 	}
37867cc92483SSepherosa Ziehau 	return 0;
37878892ea20SAggelos Economopoulos }
37888892ea20SAggelos Economopoulos 
37898892ea20SAggelos Economopoulos static void
37908892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
37918892ea20SAggelos Economopoulos {
37928892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3793c7431c78SSepherosa Ziehau 	const char *old_fw;
37948892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
37958892ea20SAggelos Economopoulos 
37968892ea20SAggelos Economopoulos 	sc->num_slices = 1;
37977cc92483SSepherosa Ziehau 
37988892ea20SAggelos Economopoulos 	/*
37997cc92483SSepherosa Ziehau 	 * XXX
38007cc92483SSepherosa Ziehau 	 *
38017cc92483SSepherosa Ziehau 	 * Don't enable multiple slices if they are not enabled,
38028892ea20SAggelos Economopoulos 	 * or if this is not an SMP system
38038892ea20SAggelos Economopoulos 	 */
3804b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
38058892ea20SAggelos Economopoulos 		return;
38068892ea20SAggelos Economopoulos 
38078892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
38088892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
38098892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
38108892ea20SAggelos Economopoulos 		return;
38118892ea20SAggelos Economopoulos 
38128892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
38138892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
38148892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
38158892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
38168892ea20SAggelos Economopoulos 	else
38178892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
38188892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
38198892ea20SAggelos Economopoulos 	if (status != 0) {
38208892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
38218892ea20SAggelos Economopoulos 		return;
38228892ea20SAggelos Economopoulos 	}
38238892ea20SAggelos Economopoulos 
38248892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
38258892ea20SAggelos Economopoulos 	   is alive */
38268892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
38278892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38288892ea20SAggelos Economopoulos 	if (status != 0) {
38298892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
38308892ea20SAggelos Economopoulos 		goto abort_with_fw;
38318892ea20SAggelos Economopoulos 	}
38328892ea20SAggelos Economopoulos 
38338892ea20SAggelos Economopoulos 	/* get rx ring size */
38348892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38358892ea20SAggelos Economopoulos 	if (status != 0) {
38368892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38378892ea20SAggelos Economopoulos 		goto abort_with_fw;
38388892ea20SAggelos Economopoulos 	}
38398892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38408892ea20SAggelos Economopoulos 
38418892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
38428892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38438892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38448892ea20SAggelos Economopoulos 	if (status != 0) {
38458892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38468892ea20SAggelos Economopoulos 		goto abort_with_fw;
38478892ea20SAggelos Economopoulos 	}
38488892ea20SAggelos Economopoulos 
38498892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
38508892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38518892ea20SAggelos Economopoulos 	if (status != 0) {
38528892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38538892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38548892ea20SAggelos Economopoulos 		goto abort_with_fw;
38558892ea20SAggelos Economopoulos 	}
38568892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
38578892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
38588892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
38598892ea20SAggelos Economopoulos 
38608892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
38618892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
3862ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
3863ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
38648892ea20SAggelos Economopoulos 	} else {
38658892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
38668892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
38678892ea20SAggelos Economopoulos 	}
38688892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
38698892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
38708892ea20SAggelos Economopoulos 		sc->num_slices--;
38718892ea20SAggelos Economopoulos 
38727cc92483SSepherosa Ziehau 	if (bootverbose)
38738892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
38748892ea20SAggelos Economopoulos 			      sc->num_slices);
38758892ea20SAggelos Economopoulos 
38768892ea20SAggelos Economopoulos 	return;
38778892ea20SAggelos Economopoulos 
38788892ea20SAggelos Economopoulos abort_with_fw:
38798892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
38808892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
38818892ea20SAggelos Economopoulos }
38828892ea20SAggelos Economopoulos 
3883a26af990SSepherosa Ziehau #if 0
38848892ea20SAggelos Economopoulos static int
38858892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
38868892ea20SAggelos Economopoulos {
38878892ea20SAggelos Economopoulos 	size_t bytes;
38888892ea20SAggelos Economopoulos 	int count, err, i, rid;
38898892ea20SAggelos Economopoulos 
38908892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
38918892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
38928892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
38938892ea20SAggelos Economopoulos 
38948892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
38958892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
38968892ea20SAggelos Economopoulos 		return ENXIO;
38978892ea20SAggelos Economopoulos 	}
38988892ea20SAggelos Economopoulos 
38998892ea20SAggelos Economopoulos 	count = sc->num_slices;
39008892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
39018892ea20SAggelos Economopoulos 	if (err != 0) {
39028892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39038892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
39048892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
39058892ea20SAggelos Economopoulos 	}
39068892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
39078892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39088892ea20SAggelos Economopoulos 			      count, sc->num_slices);
39098892ea20SAggelos Economopoulos 		device_printf(sc->dev,
39108892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
39118892ea20SAggelos Economopoulos 			      count);
39128892ea20SAggelos Economopoulos 		err = ENOSPC;
39138892ea20SAggelos Economopoulos 		goto abort_with_msix;
39148892ea20SAggelos Economopoulos 	}
39158892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
3916d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39178892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
39188892ea20SAggelos Economopoulos 		err = ENOMEM;
39198892ea20SAggelos Economopoulos 		goto abort_with_msix;
39208892ea20SAggelos Economopoulos 	}
39218892ea20SAggelos Economopoulos 
39228892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39238892ea20SAggelos Economopoulos 		rid = i + 1;
39248892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39258892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
39268892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
39278892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
39288892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
39298892ea20SAggelos Economopoulos 				      " for message %d\n", i);
39308892ea20SAggelos Economopoulos 			err = ENXIO;
39318892ea20SAggelos Economopoulos 			goto abort_with_res;
39328892ea20SAggelos Economopoulos 		}
39338892ea20SAggelos Economopoulos 	}
39348892ea20SAggelos Economopoulos 
39358892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
3936d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39378892ea20SAggelos Economopoulos 
39388892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39398892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39407d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
39417d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
39422e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
39438892ea20SAggelos Economopoulos 		if (err != 0) {
39448892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
39458892ea20SAggelos Economopoulos 				      "message %d\n", i);
39468892ea20SAggelos Economopoulos 			goto abort_with_intr;
39478892ea20SAggelos Economopoulos 		}
39488892ea20SAggelos Economopoulos 	}
39498892ea20SAggelos Economopoulos 
39507cc92483SSepherosa Ziehau 	if (bootverbose) {
39518892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
39528892ea20SAggelos Economopoulos 			      sc->num_slices);
39538892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
39546c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39556c348da6SAggelos Economopoulos 		kprintf("\n");
39568892ea20SAggelos Economopoulos 	}
39578892ea20SAggelos Economopoulos 	return (0);
39588892ea20SAggelos Economopoulos 
39598892ea20SAggelos Economopoulos abort_with_intr:
39608892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39618892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39628892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39638892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39648892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39658892ea20SAggelos Economopoulos 		}
39668892ea20SAggelos Economopoulos 	}
3967d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39688892ea20SAggelos Economopoulos 
39698892ea20SAggelos Economopoulos 
39708892ea20SAggelos Economopoulos abort_with_res:
39718892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39728892ea20SAggelos Economopoulos 		rid = i + 1;
39738892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39748892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39758892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39768892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39778892ea20SAggelos Economopoulos 	}
3978d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39798892ea20SAggelos Economopoulos 
39808892ea20SAggelos Economopoulos 
39818892ea20SAggelos Economopoulos abort_with_msix:
39828892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39838892ea20SAggelos Economopoulos 
39848892ea20SAggelos Economopoulos abort_with_msix_table:
39858892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39868892ea20SAggelos Economopoulos 			     sc->msix_table_res);
39878892ea20SAggelos Economopoulos 
39888892ea20SAggelos Economopoulos 	return err;
39898892ea20SAggelos Economopoulos }
3990a26af990SSepherosa Ziehau #endif
39918892ea20SAggelos Economopoulos 
39928892ea20SAggelos Economopoulos static int
39938892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
39948892ea20SAggelos Economopoulos {
3995cf5afd69SSepherosa Ziehau 	driver_intr_t *intr_func;
399689d55360SSepherosa Ziehau 	u_int irq_flags;
399751c70c94SSascha Wildner 
39987cc92483SSepherosa Ziehau 	sc->irq_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
39997cc92483SSepherosa Ziehau 	    &sc->irq_rid, &irq_flags);
400089d55360SSepherosa Ziehau 
400189d55360SSepherosa Ziehau 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
400289d55360SSepherosa Ziehau 	    &sc->irq_rid, irq_flags);
40038892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
40048892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
40058892ea20SAggelos Economopoulos 		return ENXIO;
40068892ea20SAggelos Economopoulos 	}
400789d55360SSepherosa Ziehau 
4008cf5afd69SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_LEGACY)
4009cf5afd69SSepherosa Ziehau 		intr_func = mxge_legacy;
4010cf5afd69SSepherosa Ziehau 	else
4011cf5afd69SSepherosa Ziehau 		intr_func = mxge_msi;
4012cf5afd69SSepherosa Ziehau 
4013798c3369SSepherosa Ziehau 	return bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE,
401426634ef8SSepherosa Ziehau 	    intr_func, &sc->ss[0], &sc->ih, &sc->main_serialize);
40158892ea20SAggelos Economopoulos }
40168892ea20SAggelos Economopoulos 
4017a26af990SSepherosa Ziehau #if 0
40188892ea20SAggelos Economopoulos static void
40198892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
40208892ea20SAggelos Economopoulos {
40218892ea20SAggelos Economopoulos 	int i, rid;
40228892ea20SAggelos Economopoulos 
40238892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40248892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
40258892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40268892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
40278892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
40288892ea20SAggelos Economopoulos 		}
40298892ea20SAggelos Economopoulos 	}
4030d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
40318892ea20SAggelos Economopoulos 
40328892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40338892ea20SAggelos Economopoulos 		rid = i + 1;
40348892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
40358892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40368892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
40378892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
40388892ea20SAggelos Economopoulos 	}
4039d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
40408892ea20SAggelos Economopoulos 
40418892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40428892ea20SAggelos Economopoulos 			     sc->msix_table_res);
40438892ea20SAggelos Economopoulos 
40448892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
40458892ea20SAggelos Economopoulos 	return;
40468892ea20SAggelos Economopoulos }
4047a26af990SSepherosa Ziehau #endif
40488892ea20SAggelos Economopoulos 
40498892ea20SAggelos Economopoulos static int
40508892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
40518892ea20SAggelos Economopoulos {
4052a26af990SSepherosa Ziehau #if 0
40538892ea20SAggelos Economopoulos 	int err;
40548892ea20SAggelos Economopoulos 
40558892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
40568892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40578892ea20SAggelos Economopoulos 	else
40588892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
40598892ea20SAggelos Economopoulos 
40608892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
40618892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
40628892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40638892ea20SAggelos Economopoulos 	}
40648892ea20SAggelos Economopoulos 	return err;
4065a26af990SSepherosa Ziehau #else
4066a26af990SSepherosa Ziehau 	return mxge_add_single_irq(sc);
4067a26af990SSepherosa Ziehau #endif
40688892ea20SAggelos Economopoulos }
40698892ea20SAggelos Economopoulos 
407026634ef8SSepherosa Ziehau static void
407126634ef8SSepherosa Ziehau mxge_setup_serialize(struct mxge_softc *sc)
407226634ef8SSepherosa Ziehau {
407326634ef8SSepherosa Ziehau 	int i = 0, slice;
407426634ef8SSepherosa Ziehau 
407526634ef8SSepherosa Ziehau 	/* Main + rx + tx */
407626634ef8SSepherosa Ziehau 	sc->nserialize = (2 * sc->num_slices) + 1;
407726634ef8SSepherosa Ziehau 	sc->serializes =
407826634ef8SSepherosa Ziehau 	    kmalloc(sc->nserialize * sizeof(struct lwkt_serialize *),
407926634ef8SSepherosa Ziehau 	        M_DEVBUF, M_WAITOK | M_ZERO);
408026634ef8SSepherosa Ziehau 
408126634ef8SSepherosa Ziehau 	/*
408226634ef8SSepherosa Ziehau 	 * Setup serializes
408326634ef8SSepherosa Ziehau 	 *
408426634ef8SSepherosa Ziehau 	 * NOTE: Order is critical
408526634ef8SSepherosa Ziehau 	 */
408626634ef8SSepherosa Ziehau 
408726634ef8SSepherosa Ziehau 	KKASSERT(i < sc->nserialize);
408826634ef8SSepherosa Ziehau 	sc->serializes[i++] = &sc->main_serialize;
408926634ef8SSepherosa Ziehau 
409026634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
409126634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
409226634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].rx_data.rx_serialize;
409326634ef8SSepherosa Ziehau 	}
409426634ef8SSepherosa Ziehau 
409526634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
409626634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
409726634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].tx.tx_serialize;
409826634ef8SSepherosa Ziehau 	}
409926634ef8SSepherosa Ziehau 
410026634ef8SSepherosa Ziehau 	KKASSERT(i == sc->nserialize);
410126634ef8SSepherosa Ziehau }
410226634ef8SSepherosa Ziehau 
410326634ef8SSepherosa Ziehau static void
410426634ef8SSepherosa Ziehau mxge_serialize(struct ifnet *ifp, enum ifnet_serialize slz)
410526634ef8SSepherosa Ziehau {
410626634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
410726634ef8SSepherosa Ziehau 
410826634ef8SSepherosa Ziehau 	ifnet_serialize_array_enter(sc->serializes, sc->nserialize, slz);
410926634ef8SSepherosa Ziehau }
411026634ef8SSepherosa Ziehau 
411126634ef8SSepherosa Ziehau static void
411226634ef8SSepherosa Ziehau mxge_deserialize(struct ifnet *ifp, enum ifnet_serialize slz)
411326634ef8SSepherosa Ziehau {
411426634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
411526634ef8SSepherosa Ziehau 
411626634ef8SSepherosa Ziehau 	ifnet_serialize_array_exit(sc->serializes, sc->nserialize, slz);
411726634ef8SSepherosa Ziehau }
411826634ef8SSepherosa Ziehau 
411926634ef8SSepherosa Ziehau static int
412026634ef8SSepherosa Ziehau mxge_tryserialize(struct ifnet *ifp, enum ifnet_serialize slz)
412126634ef8SSepherosa Ziehau {
412226634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
412326634ef8SSepherosa Ziehau 
412426634ef8SSepherosa Ziehau 	return ifnet_serialize_array_try(sc->serializes, sc->nserialize, slz);
412526634ef8SSepherosa Ziehau }
412626634ef8SSepherosa Ziehau 
412726634ef8SSepherosa Ziehau #ifdef INVARIANTS
412826634ef8SSepherosa Ziehau 
412926634ef8SSepherosa Ziehau static void
413026634ef8SSepherosa Ziehau mxge_serialize_assert(struct ifnet *ifp, enum ifnet_serialize slz,
413126634ef8SSepherosa Ziehau     boolean_t serialized)
413226634ef8SSepherosa Ziehau {
413326634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
413426634ef8SSepherosa Ziehau 
413526634ef8SSepherosa Ziehau 	ifnet_serialize_array_assert(sc->serializes, sc->nserialize,
413626634ef8SSepherosa Ziehau 	    slz, serialized);
413726634ef8SSepherosa Ziehau }
413826634ef8SSepherosa Ziehau 
413926634ef8SSepherosa Ziehau #endif	/* INVARIANTS */
414026634ef8SSepherosa Ziehau 
41418892ea20SAggelos Economopoulos static int
41428892ea20SAggelos Economopoulos mxge_attach(device_t dev)
41438892ea20SAggelos Economopoulos {
41448892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4145137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
41468892ea20SAggelos Economopoulos 	int err, rid;
41478892ea20SAggelos Economopoulos 
4148f0115d64SAggelos Economopoulos 	/*
41497cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4150f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4151f0115d64SAggelos Economopoulos 	 */
4152f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
41538892ea20SAggelos Economopoulos 	sc->dev = dev;
41547cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
4155798c3369SSepherosa Ziehau 	ifmedia_init(&sc->media, 0, mxge_media_change, mxge_media_status);
41567cc92483SSepherosa Ziehau 
415726634ef8SSepherosa Ziehau 	lwkt_serialize_init(&sc->main_serialize);
415826634ef8SSepherosa Ziehau 
41598892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
41608892ea20SAggelos Economopoulos 
41618892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
41628892ea20SAggelos Economopoulos 				 1,			/* alignment */
41638892ea20SAggelos Economopoulos 				 0,			/* boundary */
41648892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
41658892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
41668892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
41677cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
41687cc92483SSepherosa Ziehau 				 0, 			/* num segs */
41697cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
41708892ea20SAggelos Economopoulos 				 0,			/* flags */
41718892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
41728892ea20SAggelos Economopoulos 	if (err != 0) {
4173798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4174798c3369SSepherosa Ziehau 		goto failed;
41758892ea20SAggelos Economopoulos 	}
41768892ea20SAggelos Economopoulos 
4177e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
41788892ea20SAggelos Economopoulos 
41798892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
41808892ea20SAggelos Economopoulos 
41817cc92483SSepherosa Ziehau 	/*
41827cc92483SSepherosa Ziehau 	 * Map the board into the kernel
41837cc92483SSepherosa Ziehau 	 */
41848892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
41857cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
41867cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
41878892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
41888892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
41898892ea20SAggelos Economopoulos 		err = ENXIO;
4190798c3369SSepherosa Ziehau 		goto failed;
41918892ea20SAggelos Economopoulos 	}
41927cc92483SSepherosa Ziehau 
41938892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
41948892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
41958892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
41968892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
41978892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
41988892ea20SAggelos Economopoulos 		err = ENXIO;
4199798c3369SSepherosa Ziehau 		goto failed;
42008892ea20SAggelos Economopoulos 	}
42018892ea20SAggelos Economopoulos 
42027cc92483SSepherosa Ziehau 	/*
42037cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
42047cc92483SSepherosa Ziehau 	 * lanai SRAM
42057cc92483SSepherosa Ziehau 	 */
42068892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
42078892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
42088892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
42098892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
42107cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
42118892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4212798c3369SSepherosa Ziehau 	if (err != 0) {
4213798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4214798c3369SSepherosa Ziehau 		goto failed;
4215798c3369SSepherosa Ziehau 	}
42168892ea20SAggelos Economopoulos 
42177cc92483SSepherosa Ziehau 	/*
42187cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
42197cc92483SSepherosa Ziehau 	 */
42208892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
42218892ea20SAggelos Economopoulos 
42227cc92483SSepherosa Ziehau 	/*
42237cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
42247cc92483SSepherosa Ziehau 	 */
42257cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4226798c3369SSepherosa Ziehau 	if (err != 0) {
4227798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4228798c3369SSepherosa Ziehau 		goto failed;
4229798c3369SSepherosa Ziehau 	}
42307cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
42317cc92483SSepherosa Ziehau 
42328892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4233798c3369SSepherosa Ziehau 	if (err != 0) {
4234798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4235798c3369SSepherosa Ziehau 		goto failed;
4236798c3369SSepherosa Ziehau 	}
42378892ea20SAggelos Economopoulos 
42388892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4239798c3369SSepherosa Ziehau 	if (err != 0) {
4240798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4241798c3369SSepherosa Ziehau 		goto failed;
4242798c3369SSepherosa Ziehau 	}
42438892ea20SAggelos Economopoulos 
42447cc92483SSepherosa Ziehau 	/* Select & load the firmware */
42458892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4246798c3369SSepherosa Ziehau 	if (err != 0) {
4247798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4248798c3369SSepherosa Ziehau 		goto failed;
4249798c3369SSepherosa Ziehau 	}
42508892ea20SAggelos Economopoulos 
42518892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
42528892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4253798c3369SSepherosa Ziehau 	if (err != 0) {
4254798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4255798c3369SSepherosa Ziehau 		goto failed;
4256798c3369SSepherosa Ziehau 	}
42578892ea20SAggelos Economopoulos 
425826634ef8SSepherosa Ziehau 	/* Setup serializes */
425926634ef8SSepherosa Ziehau 	mxge_setup_serialize(sc);
426026634ef8SSepherosa Ziehau 
42618892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4262798c3369SSepherosa Ziehau 	if (err != 0) {
4263798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4264798c3369SSepherosa Ziehau 		goto failed;
4265798c3369SSepherosa Ziehau 	}
42668892ea20SAggelos Economopoulos 
42678892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
42688892ea20SAggelos Economopoulos 	if (err != 0) {
4269798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4270798c3369SSepherosa Ziehau 		goto failed;
42718892ea20SAggelos Economopoulos 	}
42728892ea20SAggelos Economopoulos 
42738892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
427489d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
42758892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
427689d55360SSepherosa Ziehau 
427789d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
427889d55360SSepherosa Ziehau #if 0
427989d55360SSepherosa Ziehau 	/* Well, its software, sigh */
428089d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
428189d55360SSepherosa Ziehau #endif
42828892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
428389d55360SSepherosa Ziehau 
42848892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
42858892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
428689d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
42878892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
42888892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
4289ca8ca004SSepherosa Ziehau 	ifp->if_watchdog = mxge_watchdog;
429026634ef8SSepherosa Ziehau 	ifp->if_serialize = mxge_serialize;
429126634ef8SSepherosa Ziehau 	ifp->if_deserialize = mxge_deserialize;
429226634ef8SSepherosa Ziehau 	ifp->if_tryserialize = mxge_tryserialize;
429326634ef8SSepherosa Ziehau #ifdef INVARIANTS
429426634ef8SSepherosa Ziehau 	ifp->if_serialize_assert = mxge_serialize_assert;
429526634ef8SSepherosa Ziehau #endif
429689d55360SSepherosa Ziehau 
4297820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4298820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4299820e213fSSepherosa Ziehau 
43008892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
430189d55360SSepherosa Ziehau 	mxge_media_init(sc);
43028892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
430389d55360SSepherosa Ziehau 
4304cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
430589d55360SSepherosa Ziehau 
4306b9a8961fSSepherosa Ziehau 	/*
4307b9a8961fSSepherosa Ziehau 	 * XXX
4308b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4309b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4310b9a8961fSSepherosa Ziehau 	 */
4311b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4312b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
431389d55360SSepherosa Ziehau 	sc->dying = 0;
431489d55360SSepherosa Ziehau 
4315369c353eSAggelos Economopoulos 	/* must come after ether_ifattach() */
4316369c353eSAggelos Economopoulos 	err = mxge_add_irq(sc);
4317369c353eSAggelos Economopoulos 	if (err != 0) {
4318798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4319798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4320798c3369SSepherosa Ziehau 		goto failed;
4321369c353eSAggelos Economopoulos 	}
432226634ef8SSepherosa Ziehau 
432389d55360SSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->irq_res));
432426634ef8SSepherosa Ziehau 	ifq_set_hw_serialize(&ifp->if_snd, &sc->ss[0].tx.tx_serialize);
43258892ea20SAggelos Economopoulos 
43268892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
432789d55360SSepherosa Ziehau 
4328*c9317e74SSepherosa Ziehau 	callout_reset_bycpu(&sc->co_hdl, mxge_ticks, mxge_tick, sc,
4329*c9317e74SSepherosa Ziehau 	    rman_get_cpuid(sc->irq_res));
43308892ea20SAggelos Economopoulos 	return 0;
43318892ea20SAggelos Economopoulos 
4332798c3369SSepherosa Ziehau failed:
4333798c3369SSepherosa Ziehau 	mxge_detach(dev);
43348892ea20SAggelos Economopoulos 	return err;
43358892ea20SAggelos Economopoulos }
43368892ea20SAggelos Economopoulos 
43378892ea20SAggelos Economopoulos static int
43388892ea20SAggelos Economopoulos mxge_detach(device_t dev)
43398892ea20SAggelos Economopoulos {
43408892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
43418892ea20SAggelos Economopoulos 
4342798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4343798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
4344798c3369SSepherosa Ziehau 
434526634ef8SSepherosa Ziehau 		ifnet_serialize_all(ifp);
4346798c3369SSepherosa Ziehau 
43478892ea20SAggelos Economopoulos 		sc->dying = 1;
4348798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
434989d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4350e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4351798c3369SSepherosa Ziehau 
4352798c3369SSepherosa Ziehau 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
4353798c3369SSepherosa Ziehau 
435426634ef8SSepherosa Ziehau 		ifnet_deserialize_all(ifp);
4355e3dc37faSAggelos Economopoulos 
435689d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
435789d55360SSepherosa Ziehau 
4358798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4359798c3369SSepherosa Ziehau 	}
43608892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4361798c3369SSepherosa Ziehau 
4362798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4363798c3369SSepherosa Ziehau 	    sc->sram != NULL)
43648892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4365798c3369SSepherosa Ziehau 
43668892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
43678892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4368798c3369SSepherosa Ziehau 
4369798c3369SSepherosa Ziehau 	/* MUST after sysctls and rings are freed */
43708892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4371798c3369SSepherosa Ziehau 
4372798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
43738892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4374798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
43758892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4376798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
43778892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4378798c3369SSepherosa Ziehau 
4379798c3369SSepherosa Ziehau 	if (sc->irq_res != NULL) {
4380798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
4381798c3369SSepherosa Ziehau 		    sc->irq_res);
4382798c3369SSepherosa Ziehau 	}
4383798c3369SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_MSI)
4384798c3369SSepherosa Ziehau 		pci_release_msi(dev);
4385798c3369SSepherosa Ziehau 
4386798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4387798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4388798c3369SSepherosa Ziehau 		    sc->mem_res);
4389798c3369SSepherosa Ziehau 	}
4390798c3369SSepherosa Ziehau 
4391798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
43928892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4393798c3369SSepherosa Ziehau 
43948892ea20SAggelos Economopoulos 	return 0;
43958892ea20SAggelos Economopoulos }
43968892ea20SAggelos Economopoulos 
43978892ea20SAggelos Economopoulos static int
43988892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
43998892ea20SAggelos Economopoulos {
44008892ea20SAggelos Economopoulos 	return 0;
44018892ea20SAggelos Economopoulos }
4402