xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision fb39a573)
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++) {
1162414caf0dSSepherosa Ziehau 			ss = &sc->ss[slice];
1163414caf0dSSepherosa Ziehau 
1164414caf0dSSepherosa Ziehau 			rx_done = &ss->rx_data.rx_done;
11658892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
1166414caf0dSSepherosa Ziehau 
11677cc92483SSepherosa Ziehau 			cmd.data0 =
1168414caf0dSSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
11697cc92483SSepherosa Ziehau 			cmd.data1 =
1170414caf0dSSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
11718892ea20SAggelos Economopoulos 			cmd.data2 = slice;
11727cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
11738892ea20SAggelos Economopoulos 			    &cmd);
11748892ea20SAggelos Economopoulos 		}
11758892ea20SAggelos Economopoulos 	}
11768892ea20SAggelos Economopoulos 
11777cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
11787cc92483SSepherosa Ziehau 	    &cmd);
11798892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11808892ea20SAggelos Economopoulos 
11818892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11828892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11838892ea20SAggelos Economopoulos 
11847cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
11858892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
11867cc92483SSepherosa Ziehau 
11878892ea20SAggelos Economopoulos 	if (status != 0) {
11886ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
11898892ea20SAggelos Economopoulos 		return status;
11908892ea20SAggelos Economopoulos 	}
11918892ea20SAggelos Economopoulos 
11928892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11938892ea20SAggelos Economopoulos 
11947cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
11957cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
11968892ea20SAggelos Economopoulos 
11978892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
11988892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
11998892ea20SAggelos Economopoulos 
12008892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
12017cc92483SSepherosa Ziehau 
12027cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12039a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.idx = 0;
12048892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12058892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12068892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12078892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12088892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12098892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12109a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.cnt = 0;
12119a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.cnt = 0;
12127cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12137cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12148892ea20SAggelos Economopoulos 	}
12158892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12167cc92483SSepherosa Ziehau 
12178892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12188892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12198892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12208892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12217cc92483SSepherosa Ziehau 
122289d55360SSepherosa Ziehau 	if (sc->throttle) {
122389d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12247cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12256ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
122689d55360SSepherosa Ziehau 	}
12278892ea20SAggelos Economopoulos 	return status;
12288892ea20SAggelos Economopoulos }
12298892ea20SAggelos Economopoulos 
12308892ea20SAggelos Economopoulos static int
123189d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
123289d55360SSepherosa Ziehau {
123389d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
123489d55360SSepherosa Ziehau 	mxge_softc_t *sc;
123589d55360SSepherosa Ziehau 	int err;
123689d55360SSepherosa Ziehau 	unsigned int throttle;
123789d55360SSepherosa Ziehau 
123889d55360SSepherosa Ziehau 	sc = arg1;
123989d55360SSepherosa Ziehau 	throttle = sc->throttle;
124089d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12415a637e78SSepherosa Ziehau 	if (err != 0)
124289d55360SSepherosa Ziehau 		return err;
124389d55360SSepherosa Ziehau 
124489d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
124589d55360SSepherosa Ziehau 		return 0;
124689d55360SSepherosa Ziehau 
124789d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
124889d55360SSepherosa Ziehau 		return EINVAL;
124989d55360SSepherosa Ziehau 
125026634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
125189d55360SSepherosa Ziehau 
125289d55360SSepherosa Ziehau 	cmd.data0 = throttle;
125389d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
125489d55360SSepherosa Ziehau 	if (err == 0)
125589d55360SSepherosa Ziehau 		sc->throttle = throttle;
125689d55360SSepherosa Ziehau 
125726634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
125889d55360SSepherosa Ziehau 	return err;
125989d55360SSepherosa Ziehau }
126089d55360SSepherosa Ziehau 
126189d55360SSepherosa Ziehau static int
12628892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
12638892ea20SAggelos Economopoulos {
12648892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12658892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
12668892ea20SAggelos Economopoulos 	int err;
12678892ea20SAggelos Economopoulos 
12688892ea20SAggelos Economopoulos 	sc = arg1;
12698892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
12708892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
12715a637e78SSepherosa Ziehau 	if (err != 0)
12728892ea20SAggelos Economopoulos 		return err;
12735a637e78SSepherosa Ziehau 
12748892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
12758892ea20SAggelos Economopoulos 		return 0;
12768892ea20SAggelos Economopoulos 
12778892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
12788892ea20SAggelos Economopoulos 		return EINVAL;
12798892ea20SAggelos Economopoulos 
128026634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
128189d55360SSepherosa Ziehau 
12828892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
12838892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
12848892ea20SAggelos Economopoulos 
128526634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
12868892ea20SAggelos Economopoulos 	return err;
12878892ea20SAggelos Economopoulos }
12888892ea20SAggelos Economopoulos 
12898892ea20SAggelos Economopoulos static int
12908892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
12918892ea20SAggelos Economopoulos {
12928892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12938892ea20SAggelos Economopoulos 	unsigned int enabled;
12948892ea20SAggelos Economopoulos 	int err;
12958892ea20SAggelos Economopoulos 
12968892ea20SAggelos Economopoulos 	sc = arg1;
12978892ea20SAggelos Economopoulos 	enabled = sc->pause;
12988892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
12995a637e78SSepherosa Ziehau 	if (err != 0)
13008892ea20SAggelos Economopoulos 		return err;
13015a637e78SSepherosa Ziehau 
13028892ea20SAggelos Economopoulos 	if (enabled == sc->pause)
13038892ea20SAggelos Economopoulos 		return 0;
13048892ea20SAggelos Economopoulos 
130526634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
13068892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
130726634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13088892ea20SAggelos Economopoulos 
13098892ea20SAggelos Economopoulos 	return err;
13108892ea20SAggelos Economopoulos }
13118892ea20SAggelos Economopoulos 
13128892ea20SAggelos Economopoulos static int
13138892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13148892ea20SAggelos Economopoulos {
13158892ea20SAggelos Economopoulos 	int err;
13168892ea20SAggelos Economopoulos 
13178892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13188892ea20SAggelos Economopoulos 		return EFAULT;
13198892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13208892ea20SAggelos Economopoulos 	arg1 = NULL;
13218892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13228892ea20SAggelos Economopoulos 
13238892ea20SAggelos Economopoulos 	return err;
13248892ea20SAggelos Economopoulos }
13258892ea20SAggelos Economopoulos 
13268892ea20SAggelos Economopoulos static void
13278892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13288892ea20SAggelos Economopoulos {
1329798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13308892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13318892ea20SAggelos Economopoulos 		int slice;
13328892ea20SAggelos Economopoulos 
13338892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13348892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1335798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13368892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13378892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13388892ea20SAggelos Economopoulos 			}
1339798c3369SSepherosa Ziehau 		}
1340798c3369SSepherosa Ziehau 	}
1341798c3369SSepherosa Ziehau 
1342798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13438892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13448892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1345798c3369SSepherosa Ziehau 	}
1346798c3369SSepherosa Ziehau 
1347798c3369SSepherosa Ziehau 	if (sc->sysctl_tree != NULL) {
1348bbac37fbSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1349bbac37fbSAggelos Economopoulos 		sc->sysctl_tree = NULL;
13508892ea20SAggelos Economopoulos 	}
1351798c3369SSepherosa Ziehau }
13528892ea20SAggelos Economopoulos 
13538892ea20SAggelos Economopoulos static void
13548892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13558892ea20SAggelos Economopoulos {
13568892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13578892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13588892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13598892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13608892ea20SAggelos Economopoulos 	int slice;
13618892ea20SAggelos Economopoulos 	char slice_num[8];
13628892ea20SAggelos Economopoulos 
1363b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1364b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1365b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
13667cc92483SSepherosa Ziehau 	    OID_AUTO, device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "");
1367b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1368b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1369b6737651SAggelos Economopoulos 		return;
1370b6737651SAggelos Economopoulos 	}
1371b6737651SAggelos Economopoulos 
1372b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
13738892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13748892ea20SAggelos Economopoulos 
13757cc92483SSepherosa Ziehau 	/*
13767cc92483SSepherosa Ziehau 	 * Random information
13777cc92483SSepherosa Ziehau 	 */
13787cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13797cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
13808892ea20SAggelos Economopoulos 
13817cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
13827cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
13838892ea20SAggelos Economopoulos 
13847cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
13857cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
13868892ea20SAggelos Economopoulos 
13877cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
13887cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
138989d55360SSepherosa Ziehau 
13907cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
13917cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
13928892ea20SAggelos Economopoulos 
13937cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
13947cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
13958892ea20SAggelos Economopoulos 
13967cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
13977cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
13988892ea20SAggelos Economopoulos 
13997cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
14007cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
14018892ea20SAggelos Economopoulos 
14027cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14037cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14047cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14057cc92483SSepherosa Ziehau 
14067cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14077cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14087cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14097cc92483SSepherosa Ziehau 
14107cc92483SSepherosa Ziehau 	/*
14117cc92483SSepherosa Ziehau 	 * Performance related tunables
14127cc92483SSepherosa Ziehau 	 */
14137cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14147cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14157cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14167cc92483SSepherosa Ziehau 
14177cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14187cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14197cc92483SSepherosa Ziehau 	    "Transmit throttling");
14207cc92483SSepherosa Ziehau 
14217cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "flow_control_enabled",
14227cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_flow_control, "I",
14237cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14247cc92483SSepherosa Ziehau 
14257cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14267cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14277cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14287cc92483SSepherosa Ziehau 
14297cc92483SSepherosa Ziehau 	/*
14307cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14317cc92483SSepherosa Ziehau 	 * Need to swap it
14327cc92483SSepherosa Ziehau 	 */
14337cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14347cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14357cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14367cc92483SSepherosa Ziehau 
14377cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14387cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14397cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14407cc92483SSepherosa Ziehau 
14417cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14427cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14437cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14447cc92483SSepherosa Ziehau 
14457cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14467cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14477cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14487cc92483SSepherosa Ziehau 
14497cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14507cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14517cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14527cc92483SSepherosa Ziehau 
14537cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14547cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14557cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14567cc92483SSepherosa Ziehau 
14577cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14587cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14597cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14607cc92483SSepherosa Ziehau 
14617cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14627cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14637cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14647cc92483SSepherosa Ziehau 
14657cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14667cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14677cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14687cc92483SSepherosa Ziehau 
14697cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14707cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14717cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14727cc92483SSepherosa Ziehau 
14737cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14747cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14757cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14767cc92483SSepherosa Ziehau 
14777cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14787cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14797cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
14807cc92483SSepherosa Ziehau 
14817cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
14827cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
14837cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
14848892ea20SAggelos Economopoulos 
14858892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
14868892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
14877cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
14887cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1489798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1490798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1491798c3369SSepherosa Ziehau 		return;
1492798c3369SSepherosa Ziehau 	}
14938892ea20SAggelos Economopoulos 
14948892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
14958892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
14968892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
14978892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
14988892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1499b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
15007cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15017cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1502798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1503798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1504798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1505798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1506798c3369SSepherosa Ziehau 		}
15078892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15087cc92483SSepherosa Ziehau 
15097cc92483SSepherosa Ziehau 		/*
15107cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15117cc92483SSepherosa Ziehau 		 */
15127cc92483SSepherosa Ziehau 
15137cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15149a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_small.cnt, 0, "rx_small_cnt");
15157cc92483SSepherosa Ziehau 
15167cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15179a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_big.cnt, 0, "rx_small_cnt");
15188892ea20SAggelos Economopoulos 
15198892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
15208892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
15218892ea20SAggelos Economopoulos 		if (slice > 0)
15228892ea20SAggelos Economopoulos 			continue;
15238892ea20SAggelos Economopoulos #endif
15248892ea20SAggelos Economopoulos 
15257cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15267cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15277cc92483SSepherosa Ziehau 
15287cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15297cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15307cc92483SSepherosa Ziehau 
15317cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15327cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15337cc92483SSepherosa Ziehau 
15347cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15357cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15367cc92483SSepherosa Ziehau 
15377cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15387cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15397cc92483SSepherosa Ziehau 
15407cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15417cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15428892ea20SAggelos Economopoulos 	}
15438892ea20SAggelos Economopoulos }
15448892ea20SAggelos Economopoulos 
154589d55360SSepherosa Ziehau /*
154689d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
154789d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
154889d55360SSepherosa Ziehau  */
1549ddbf91b7SSepherosa Ziehau static __inline void
15508892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15518892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15528892ea20SAggelos Economopoulos {
15538892ea20SAggelos Economopoulos 	int idx, starting_slot;
15545ca32f31SSepherosa Ziehau 
15558892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15568892ea20SAggelos Economopoulos 	while (cnt > 1) {
15578892ea20SAggelos Economopoulos 		cnt--;
15588892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15595ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15608892ea20SAggelos Economopoulos 		wmb();
15618892ea20SAggelos Economopoulos 	}
15628892ea20SAggelos Economopoulos }
15638892ea20SAggelos Economopoulos 
15648892ea20SAggelos Economopoulos /*
156589d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15668892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15678892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15688892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15698892ea20SAggelos Economopoulos  */
1570ddbf91b7SSepherosa Ziehau static __inline void
157189d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15728892ea20SAggelos Economopoulos {
15738892ea20SAggelos Economopoulos 	int idx, i;
15748892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15758892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15768892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15778892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15788892ea20SAggelos Economopoulos 	uint8_t last_flags;
15798892ea20SAggelos Economopoulos 
15808892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15818892ea20SAggelos Economopoulos 
15828892ea20SAggelos Economopoulos 	last_flags = src->flags;
15838892ea20SAggelos Economopoulos 	src->flags = 0;
15848892ea20SAggelos Economopoulos 	wmb();
15858892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
15868892ea20SAggelos Economopoulos 	srcp = src;
15878892ea20SAggelos Economopoulos 
15888892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
15895ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
15908892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
15918892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
15928892ea20SAggelos Economopoulos 			srcp += 2;
15938892ea20SAggelos Economopoulos 			dstp += 2;
15948892ea20SAggelos Economopoulos 		}
15958892ea20SAggelos Economopoulos 	} else {
15965ca32f31SSepherosa Ziehau 		/*
15975ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
15985ca32f31SSepherosa Ziehau 		 * that it is submitted below
15995ca32f31SSepherosa Ziehau 		 */
16008892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16018892ea20SAggelos Economopoulos 		i = 0;
16028892ea20SAggelos Economopoulos 	}
16038892ea20SAggelos Economopoulos 	if (i < cnt) {
16045ca32f31SSepherosa Ziehau 		/* Submit the first request */
16058892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16068892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16078892ea20SAggelos Economopoulos 	}
16088892ea20SAggelos Economopoulos 
16095ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16108892ea20SAggelos Economopoulos 	src->flags = last_flags;
16118892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16128892ea20SAggelos Economopoulos 	src_ints+=3;
16138892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16148892ea20SAggelos Economopoulos 	dst_ints+=3;
16158892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16168892ea20SAggelos Economopoulos 	tx->req += cnt;
16178892ea20SAggelos Economopoulos 	wmb();
16188892ea20SAggelos Economopoulos }
16198892ea20SAggelos Economopoulos 
162089d55360SSepherosa Ziehau static int
162189d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
162289d55360SSepherosa Ziehau {
162389d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
162489d55360SSepherosa Ziehau 	struct mbuf *m;
162589d55360SSepherosa Ziehau 
162689d55360SSepherosa Ziehau 	m = *mp;
162789d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
162889d55360SSepherosa Ziehau 
162989d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
163089d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
163189d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
163289d55360SSepherosa Ziehau 
163389d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
163489d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
163589d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
163689d55360SSepherosa Ziehau 
163789d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
163889d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
163989d55360SSepherosa Ziehau 		if (m == NULL) {
164089d55360SSepherosa Ziehau 			*mp = NULL;
164189d55360SSepherosa Ziehau 			return ENOBUFS;
164289d55360SSepherosa Ziehau 		}
164389d55360SSepherosa Ziehau 		*mp = m;
164489d55360SSepherosa Ziehau 	}
164589d55360SSepherosa Ziehau 	return 0;
164689d55360SSepherosa Ziehau }
16478892ea20SAggelos Economopoulos 
1648ca8ca004SSepherosa Ziehau static int
1649*fb39a573SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mxge_buffer_state *info_map,
165048d12a0bSSepherosa Ziehau     struct mbuf *m, int busdma_seg_cnt)
16518892ea20SAggelos Economopoulos {
16528892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16538892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16548892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16558892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16568892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16578892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16588892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
1659*fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_last;
166048d12a0bSSepherosa Ziehau 	bus_dmamap_t map = info_map->map;
16618892ea20SAggelos Economopoulos 
16628892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16638892ea20SAggelos Economopoulos 
16645ca32f31SSepherosa Ziehau 	/*
16655ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16665ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16678892ea20SAggelos Economopoulos 	 */
166889d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
166989d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16708892ea20SAggelos Economopoulos 
16715ca32f31SSepherosa Ziehau 	/*
16725ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16735ca32f31SSepherosa Ziehau 	 */
167489d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16758892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16768892ea20SAggelos Economopoulos 
16775ca32f31SSepherosa Ziehau 	/*
16785ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16795ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
16805ca32f31SSepherosa Ziehau 	 */
16818892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16828892ea20SAggelos Economopoulos 
16838892ea20SAggelos Economopoulos 	req = tx->req_list;
16848892ea20SAggelos Economopoulos 	seg = tx->seg_list;
16858892ea20SAggelos Economopoulos 	cnt = 0;
16868892ea20SAggelos Economopoulos 	rdma_count = 0;
16875ca32f31SSepherosa Ziehau 
16885ca32f31SSepherosa Ziehau 	/*
16895ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
16905ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
16915ca32f31SSepherosa Ziehau 	 * this is equal to "count".
16928892ea20SAggelos Economopoulos 	 *
16935ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
16945ca32f31SSepherosa Ziehau 	 * segment cut.
16958892ea20SAggelos Economopoulos 	 *
16965ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
16975ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
16985ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
16995ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
17005ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
17015ca32f31SSepherosa Ziehau 	 * RDMA.
17025ca32f31SSepherosa Ziehau 	 *
17035ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
17045ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17055ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17068892ea20SAggelos Economopoulos 	 */
17078892ea20SAggelos Economopoulos 
17088892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17095ca32f31SSepherosa Ziehau 		/*
17105ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17115ca32f31SSepherosa Ziehau 		 */
17128892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17138892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17148892ea20SAggelos Economopoulos 		len = seg->ds_len;
17158892ea20SAggelos Economopoulos 
17168892ea20SAggelos Economopoulos 		while (len) {
17178892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17188892ea20SAggelos Economopoulos 			seglen = len;
17198892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17208892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17218892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17225ca32f31SSepherosa Ziehau 				/* Payload */
17238892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17248892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17258892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17268892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17275ca32f31SSepherosa Ziehau 				flags_next |=
17285ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17298892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17308892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17318892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17325ca32f31SSepherosa Ziehau 				/* Header ends */
17338892ea20SAggelos Economopoulos 				rdma_count = -1;
17348892ea20SAggelos Economopoulos 				cum_len_next = 0;
17358892ea20SAggelos Economopoulos 				seglen = -cum_len;
17368892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17378892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17388892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17398892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17408892ea20SAggelos Economopoulos 			}
17418892ea20SAggelos Economopoulos 
17428892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17438892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17448892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17458892ea20SAggelos Economopoulos 			req->pad = 0;
17468892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17478892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17488892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17495ca32f31SSepherosa Ziehau 			req->flags =
17505ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17518892ea20SAggelos Economopoulos 			low += seglen;
17528892ea20SAggelos Economopoulos 			len -= seglen;
17538892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17548892ea20SAggelos Economopoulos 			flags = flags_next;
17558892ea20SAggelos Economopoulos 			req++;
17568892ea20SAggelos Economopoulos 			cnt++;
17578892ea20SAggelos Economopoulos 			rdma_count++;
17588892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17598892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17608892ea20SAggelos Economopoulos 			else
17618892ea20SAggelos Economopoulos 				cksum_offset = 0;
17628892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17638892ea20SAggelos Economopoulos 				goto drop;
17648892ea20SAggelos Economopoulos 		}
17658892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17668892ea20SAggelos Economopoulos 		seg++;
17678892ea20SAggelos Economopoulos 	}
17688892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17698892ea20SAggelos Economopoulos 
17708892ea20SAggelos Economopoulos 	do {
17718892ea20SAggelos Economopoulos 		req--;
17728892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17738892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17748892ea20SAggelos Economopoulos 
177548d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
177648d12a0bSSepherosa Ziehau 
177748d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
177848d12a0bSSepherosa Ziehau 	info_last->map = map;
177948d12a0bSSepherosa Ziehau 	info_last->m = m;
178048d12a0bSSepherosa Ziehau 
17818892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17828892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
17838892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
17848892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
17858892ea20SAggelos Economopoulos 		*tx->send_go = 1;
17868892ea20SAggelos Economopoulos 		tx->queue_active = 1;
17878892ea20SAggelos Economopoulos 		tx->activate++;
17888892ea20SAggelos Economopoulos 		wmb();
17898892ea20SAggelos Economopoulos 	}
17908892ea20SAggelos Economopoulos #endif
1791ca8ca004SSepherosa Ziehau 	return 0;
17928892ea20SAggelos Economopoulos 
17938892ea20SAggelos Economopoulos drop:
17948892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
17958892ea20SAggelos Economopoulos 	m_freem(m);
1796ca8ca004SSepherosa Ziehau 	return ENOBUFS;
17978892ea20SAggelos Economopoulos }
17988892ea20SAggelos Economopoulos 
1799ca8ca004SSepherosa Ziehau static int
18005da1e9c3SSepherosa Ziehau mxge_encap(mxge_tx_ring_t *tx, struct mbuf *m, bus_addr_t zeropad)
18018892ea20SAggelos Economopoulos {
18028892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
18038892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
180448d12a0bSSepherosa Ziehau 	bus_dmamap_t map;
180589d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
18068892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
18078892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
1808*fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_map, *info_last;
18098892ea20SAggelos Economopoulos 
181089d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1811ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1812ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1813ca8ca004SSepherosa Ziehau 			return err;
18148892ea20SAggelos Economopoulos 	}
181589d55360SSepherosa Ziehau 
18165ca32f31SSepherosa Ziehau 	/*
18175ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18185ca32f31SSepherosa Ziehau 	 */
181989d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
182048d12a0bSSepherosa Ziehau 	info_map = &tx->info[idx];
182148d12a0bSSepherosa Ziehau 	map = info_map->map;
182248d12a0bSSepherosa Ziehau 
182348d12a0bSSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, map, &m,
182489d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
182589d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
182689d55360SSepherosa Ziehau 		goto drop;
182748d12a0bSSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, map, BUS_DMASYNC_PREWRITE);
182889d55360SSepherosa Ziehau 
18295ca32f31SSepherosa Ziehau 	/*
18305ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18315ca32f31SSepherosa Ziehau 	 */
1832ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
183348d12a0bSSepherosa Ziehau 		return mxge_encap_tso(tx, info_map, m, cnt);
18348892ea20SAggelos Economopoulos 
18358892ea20SAggelos Economopoulos 	req = tx->req_list;
18368892ea20SAggelos Economopoulos 	cksum_offset = 0;
18378892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18388892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18398892ea20SAggelos Economopoulos 
18405ca32f31SSepherosa Ziehau 	/*
18415ca32f31SSepherosa Ziehau 	 * Checksum offloading
18425ca32f31SSepherosa Ziehau 	 */
184389d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
184489d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18458892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18468892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18478892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18488892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18498892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18508892ea20SAggelos Economopoulos 	} else {
18518892ea20SAggelos Economopoulos 		odd_flag = 0;
18528892ea20SAggelos Economopoulos 	}
18538892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18548892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18558892ea20SAggelos Economopoulos 
18565ca32f31SSepherosa Ziehau 	/*
18575ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18585ca32f31SSepherosa Ziehau 	 */
18598892ea20SAggelos Economopoulos 	cum_len = 0;
18608892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18618892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18628892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18635ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18645ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18658892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18668892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18678892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18688892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18698892ea20SAggelos Economopoulos 		else
18708892ea20SAggelos Economopoulos 			cksum_offset = 0;
18718892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18728892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18738892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18748892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18758892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18768892ea20SAggelos Economopoulos 		seg++;
18778892ea20SAggelos Economopoulos 		req++;
18788892ea20SAggelos Economopoulos 		req->flags = 0;
18798892ea20SAggelos Economopoulos 	}
18808892ea20SAggelos Economopoulos 	req--;
18815ca32f31SSepherosa Ziehau 
18825ca32f31SSepherosa Ziehau 	/*
18835ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
18845ca32f31SSepherosa Ziehau 	 */
18858892ea20SAggelos Economopoulos 	if (cum_len < 60) {
18868892ea20SAggelos Economopoulos 		req++;
18875da1e9c3SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(zeropad));
18885da1e9c3SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(zeropad));
18898892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
18908892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
18918892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18928892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18938892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18948892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18958892ea20SAggelos Economopoulos 		cnt++;
18968892ea20SAggelos Economopoulos 	}
18978892ea20SAggelos Economopoulos 
18988892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
18998892ea20SAggelos Economopoulos #if 0
19008892ea20SAggelos Economopoulos 	/* print what the firmware will see */
19018892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
19026c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
19038892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
19048892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19058892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19068892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19078892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19088892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19098892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19108892ea20SAggelos Economopoulos 	}
19116c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19128892ea20SAggelos Economopoulos #endif
191348d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
191448d12a0bSSepherosa Ziehau 
191548d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
191648d12a0bSSepherosa Ziehau 	info_last->map = map;
191748d12a0bSSepherosa Ziehau 	info_last->m = m;
191848d12a0bSSepherosa Ziehau 
19198892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19208892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19218892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19228892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19238892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19248892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19258892ea20SAggelos Economopoulos 		tx->activate++;
19268892ea20SAggelos Economopoulos 		wmb();
19278892ea20SAggelos Economopoulos 	}
19288892ea20SAggelos Economopoulos #endif
1929ca8ca004SSepherosa Ziehau 	return 0;
19308892ea20SAggelos Economopoulos 
19318892ea20SAggelos Economopoulos drop:
19328892ea20SAggelos Economopoulos 	m_freem(m);
1933ca8ca004SSepherosa Ziehau 	return err;
19348892ea20SAggelos Economopoulos }
19358892ea20SAggelos Economopoulos 
19368892ea20SAggelos Economopoulos static void
1937f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19388892ea20SAggelos Economopoulos {
19398892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
1940795c96bbSSepherosa Ziehau 	mxge_tx_ring_t *tx;
19415da1e9c3SSepherosa Ziehau 	bus_addr_t zeropad;
1942ca8ca004SSepherosa Ziehau 	int encap = 0;
19438892ea20SAggelos Economopoulos 
194426634ef8SSepherosa Ziehau 	/* XXX Only use the first slice for now */
194526634ef8SSepherosa Ziehau 	tx = &sc->ss[0].tx;
194626634ef8SSepherosa Ziehau 
1947f0a26983SSepherosa Ziehau 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
194826634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
1949795c96bbSSepherosa Ziehau 
1950795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1951795c96bbSSepherosa Ziehau 		return;
1952795c96bbSSepherosa Ziehau 
19535da1e9c3SSepherosa Ziehau 	zeropad = sc->zeropad_dma.dmem_busaddr;
1954795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1955795c96bbSSepherosa Ziehau 		struct mbuf *m;
1956ca8ca004SSepherosa Ziehau 		int error;
1957795c96bbSSepherosa Ziehau 
1958795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1959795c96bbSSepherosa Ziehau 		if (m == NULL)
1960ca8ca004SSepherosa Ziehau 			goto done;
1961795c96bbSSepherosa Ziehau 
1962795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
19635da1e9c3SSepherosa Ziehau 		error = mxge_encap(tx, m, zeropad);
1964ca8ca004SSepherosa Ziehau 		if (!error)
1965ca8ca004SSepherosa Ziehau 			encap = 1;
1966fed54363SSepherosa Ziehau 		else
1967fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1968795c96bbSSepherosa Ziehau 	}
1969795c96bbSSepherosa Ziehau 
1970795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1971795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1972ca8ca004SSepherosa Ziehau done:
1973ca8ca004SSepherosa Ziehau 	if (encap)
1974ca8ca004SSepherosa Ziehau 		ifp->if_timer = 5;
1975ca8ca004SSepherosa Ziehau }
1976ca8ca004SSepherosa Ziehau 
1977ca8ca004SSepherosa Ziehau static void
1978ca8ca004SSepherosa Ziehau mxge_watchdog(struct ifnet *ifp)
1979ca8ca004SSepherosa Ziehau {
1980ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1981ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1982ca8ca004SSepherosa Ziehau 	mxge_tx_ring_t *tx = &sc->ss[0].tx;
1983ca8ca004SSepherosa Ziehau 
198426634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
1985ca8ca004SSepherosa Ziehau 
1986ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
1987ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
1988ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
1989ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
1990ca8ca004SSepherosa Ziehau 		return;
1991ca8ca004SSepherosa Ziehau 	} else {
1992ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
1993ca8ca004SSepherosa Ziehau 		    "check link partner\n");
1994ca8ca004SSepherosa Ziehau 	}
1995ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
19968892ea20SAggelos Economopoulos }
19978892ea20SAggelos Economopoulos 
19988892ea20SAggelos Economopoulos /*
19992f47b54fSSepherosa Ziehau  * Copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
20008892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
20018892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's low
20028892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
20038892ea20SAggelos Economopoulos  * in a burst
20048892ea20SAggelos Economopoulos  */
2005ddbf91b7SSepherosa Ziehau static __inline void
20068892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
20078892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
20088892ea20SAggelos Economopoulos {
20098892ea20SAggelos Economopoulos 	uint32_t low;
20108892ea20SAggelos Economopoulos 
20118892ea20SAggelos Economopoulos 	low = src->addr_low;
20128892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
20138892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
20148892ea20SAggelos Economopoulos 	wmb();
20158892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
20168892ea20SAggelos Economopoulos 	wmb();
20178892ea20SAggelos Economopoulos 	src->addr_low = low;
20188892ea20SAggelos Economopoulos 	dst->addr_low = low;
20198892ea20SAggelos Economopoulos 	wmb();
20208892ea20SAggelos Economopoulos }
20218892ea20SAggelos Economopoulos 
20228892ea20SAggelos Economopoulos static int
20238ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20248ebf015eSSepherosa Ziehau     boolean_t init)
20258892ea20SAggelos Economopoulos {
20268892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20278892ea20SAggelos Economopoulos 	struct mbuf *m;
2028363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20298892ea20SAggelos Economopoulos 
20308ebf015eSSepherosa Ziehau 	mflag = MB_DONTWAIT;
20318ebf015eSSepherosa Ziehau 	if (__predict_false(init))
20328ebf015eSSepherosa Ziehau 		mflag = MB_WAIT;
20338ebf015eSSepherosa Ziehau 
20348ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20358892ea20SAggelos Economopoulos 	if (m == NULL) {
20368892ea20SAggelos Economopoulos 		err = ENOBUFS;
20378ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20388ebf015eSSepherosa Ziehau 			/*
20398ebf015eSSepherosa Ziehau 			 * During initialization, there
20408ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20418ebf015eSSepherosa Ziehau 			 */
20428ebf015eSSepherosa Ziehau 			return err;
20438ebf015eSSepherosa Ziehau 		}
20448892ea20SAggelos Economopoulos 		goto done;
20458892ea20SAggelos Economopoulos 	}
20462823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20478ebf015eSSepherosa Ziehau 
20487d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20497d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20508892ea20SAggelos Economopoulos 	if (err != 0) {
20518ebf015eSSepherosa Ziehau 		m_freem(m);
20528ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20538ebf015eSSepherosa Ziehau 			/*
20548ebf015eSSepherosa Ziehau 			 * During initialization, there
20558ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20568ebf015eSSepherosa Ziehau 			 */
20578ebf015eSSepherosa Ziehau 			return err;
20588ebf015eSSepherosa Ziehau 		}
20598892ea20SAggelos Economopoulos 		goto done;
20608892ea20SAggelos Economopoulos 	}
20618ebf015eSSepherosa Ziehau 
20628892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20638ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20648ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20658892ea20SAggelos Economopoulos 
20668892ea20SAggelos Economopoulos done:
20678892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20688892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20698892ea20SAggelos Economopoulos 	return err;
20708892ea20SAggelos Economopoulos }
20718892ea20SAggelos Economopoulos 
20728892ea20SAggelos Economopoulos static int
2073363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2074363b44f8SSepherosa Ziehau     boolean_t init)
20758892ea20SAggelos Economopoulos {
2076b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20778892ea20SAggelos Economopoulos 	struct mbuf *m;
2078363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2079363b44f8SSepherosa Ziehau 
2080363b44f8SSepherosa Ziehau 	mflag = MB_DONTWAIT;
2081363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2082363b44f8SSepherosa Ziehau 		mflag = MB_WAIT;
20838892ea20SAggelos Economopoulos 
20848892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2085363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2086b9a8961fSSepherosa Ziehau 	else
2087363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
20888892ea20SAggelos Economopoulos 	if (m == NULL) {
20898892ea20SAggelos Economopoulos 		err = ENOBUFS;
2090363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2091363b44f8SSepherosa Ziehau 			/*
2092363b44f8SSepherosa Ziehau 			 * During initialization, there
2093363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2094363b44f8SSepherosa Ziehau 			 */
2095363b44f8SSepherosa Ziehau 			return err;
2096363b44f8SSepherosa Ziehau 		}
20978892ea20SAggelos Economopoulos 		goto done;
20988892ea20SAggelos Economopoulos 	}
20993ae50fbeSSepherosa Ziehau 	m->m_len = m->m_pkthdr.len = rx->cl_size;
2100b9a8961fSSepherosa Ziehau 
21017d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2102b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
21038892ea20SAggelos Economopoulos 	if (err != 0) {
2104363b44f8SSepherosa Ziehau 		m_freem(m);
2105363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2106363b44f8SSepherosa Ziehau 			/*
2107363b44f8SSepherosa Ziehau 			 * During initialization, there
2108363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2109363b44f8SSepherosa Ziehau 			 */
2110363b44f8SSepherosa Ziehau 			return err;
2111363b44f8SSepherosa Ziehau 		}
21128892ea20SAggelos Economopoulos 		goto done;
21138892ea20SAggelos Economopoulos 	}
2114b9a8961fSSepherosa Ziehau 
21158892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2116363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2117363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21188892ea20SAggelos Economopoulos 
21198892ea20SAggelos Economopoulos done:
2120b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2121b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21228892ea20SAggelos Economopoulos 	return err;
21238892ea20SAggelos Economopoulos }
21248892ea20SAggelos Economopoulos 
21258892ea20SAggelos Economopoulos /*
21268892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21278892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21288892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21298892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21308892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21318892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21328892ea20SAggelos Economopoulos  */
213352cf8dfcSSepherosa Ziehau static __inline uint16_t
21348892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21358892ea20SAggelos Economopoulos {
213652cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
213752cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21388892ea20SAggelos Economopoulos 	uint16_t c;
21398892ea20SAggelos Economopoulos 
214052cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21418892ea20SAggelos Economopoulos 
214252cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21438892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21448892ea20SAggelos Economopoulos 		return 1;
214552cf8dfcSSepherosa Ziehau 
214652cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
214752cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21488892ea20SAggelos Economopoulos 		return 1;
214952cf8dfcSSepherosa Ziehau 
21508892ea20SAggelos Economopoulos #ifdef INET
21518892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21528892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21538892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21548892ea20SAggelos Economopoulos #else
21558892ea20SAggelos Economopoulos 	c = 1;
21568892ea20SAggelos Economopoulos #endif
21578892ea20SAggelos Economopoulos 	c ^= 0xffff;
215852cf8dfcSSepherosa Ziehau 	return c;
21598892ea20SAggelos Economopoulos }
21608892ea20SAggelos Economopoulos 
21618892ea20SAggelos Economopoulos static void
21628892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21638892ea20SAggelos Economopoulos {
21648892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21658892ea20SAggelos Economopoulos 	uint32_t partial;
21668892ea20SAggelos Economopoulos 
21678892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21688892ea20SAggelos Economopoulos 
21698892ea20SAggelos Economopoulos 	/*
217052cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
217152cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21728892ea20SAggelos Economopoulos 	 * header.
21738892ea20SAggelos Economopoulos 	 */
21748892ea20SAggelos Economopoulos 
217552cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21768892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21778892ea20SAggelos Economopoulos 
217852cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
217952cf8dfcSSepherosa Ziehau 	*csum += ~partial;
218052cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
218152cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
218252cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
218352cf8dfcSSepherosa Ziehau 
218452cf8dfcSSepherosa Ziehau 	/*
218552cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
218652cf8dfcSSepherosa Ziehau 	 * later consumers expect this
218752cf8dfcSSepherosa Ziehau 	 */
21888892ea20SAggelos Economopoulos 	*csum = htons(*csum);
21898892ea20SAggelos Economopoulos 
21908892ea20SAggelos Economopoulos 	/* save the tag */
2191b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
21928892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
21938892ea20SAggelos Economopoulos 
21948892ea20SAggelos Economopoulos 	/*
21958892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
21968892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
21978892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
21988892ea20SAggelos Economopoulos 	 * type field is already in place.
21998892ea20SAggelos Economopoulos 	 */
2200b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
22018892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2202b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
22038892ea20SAggelos Economopoulos }
22048892ea20SAggelos Economopoulos 
22058892ea20SAggelos Economopoulos 
220652cf8dfcSSepherosa Ziehau static __inline void
22075da1e9c3SSepherosa Ziehau mxge_rx_done_big(struct ifnet *ifp, mxge_rx_ring_t *rx,
22085da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22098892ea20SAggelos Economopoulos {
22108892ea20SAggelos Economopoulos 	struct mbuf *m;
221152cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22128892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22138892ea20SAggelos Economopoulos 	int idx;
22148892ea20SAggelos Economopoulos 
22158892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2216b9a8961fSSepherosa Ziehau 	rx->cnt++;
221752cf8dfcSSepherosa Ziehau 
221852cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22198892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
222052cf8dfcSSepherosa Ziehau 
222152cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2222363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
222352cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2224d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22258892ea20SAggelos Economopoulos 		return;
22268892ea20SAggelos Economopoulos 	}
22278892ea20SAggelos Economopoulos 
222852cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22298892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22308892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22318892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22328892ea20SAggelos Economopoulos 
223352cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22348892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22358892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22368892ea20SAggelos Economopoulos 
223752cf8dfcSSepherosa Ziehau 	/*
223852cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
223952cf8dfcSSepherosa Ziehau 	 * aligned
224052cf8dfcSSepherosa Ziehau 	 */
22418892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22428892ea20SAggelos Economopoulos 
22438892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22448892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
224552cf8dfcSSepherosa Ziehau 
2246cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
224752cf8dfcSSepherosa Ziehau 
224852cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
224952cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22508892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
225152cf8dfcSSepherosa Ziehau 
225252cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
225389d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
225452cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
225589d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22568892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
225789d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
225889d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22598892ea20SAggelos Economopoulos 	}
2260eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
22618892ea20SAggelos Economopoulos }
22628892ea20SAggelos Economopoulos 
226352cf8dfcSSepherosa Ziehau static __inline void
22645da1e9c3SSepherosa Ziehau mxge_rx_done_small(struct ifnet *ifp, mxge_rx_ring_t *rx,
22655da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22668892ea20SAggelos Economopoulos {
226752cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22688892ea20SAggelos Economopoulos 	struct mbuf *m;
22698892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22708892ea20SAggelos Economopoulos 	int idx;
22718892ea20SAggelos Economopoulos 
22728892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22738892ea20SAggelos Economopoulos 	rx->cnt++;
227452cf8dfcSSepherosa Ziehau 
227552cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22768892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
227752cf8dfcSSepherosa Ziehau 
227852cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22798ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
228052cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2281d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22828892ea20SAggelos Economopoulos 		return;
22838892ea20SAggelos Economopoulos 	}
22848892ea20SAggelos Economopoulos 
228552cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22868892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22878892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22888892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22898892ea20SAggelos Economopoulos 
229052cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22918892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22928892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22938892ea20SAggelos Economopoulos 
229452cf8dfcSSepherosa Ziehau 	/*
229552cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
229652cf8dfcSSepherosa Ziehau 	 * aligned
229752cf8dfcSSepherosa Ziehau 	 */
22988892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22998892ea20SAggelos Economopoulos 
23008892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
23018892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
230252cf8dfcSSepherosa Ziehau 
2303cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
230452cf8dfcSSepherosa Ziehau 
230552cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
230652cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
23078892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
230852cf8dfcSSepherosa Ziehau 
230952cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
231089d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
231152cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
231289d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
23138892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
231489d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
231589d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
23168892ea20SAggelos Economopoulos 	}
2317eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
23188892ea20SAggelos Economopoulos }
23198892ea20SAggelos Economopoulos 
232052cf8dfcSSepherosa Ziehau static __inline void
23215da1e9c3SSepherosa Ziehau mxge_clean_rx_done(struct ifnet *ifp, struct mxge_rx_data *rx_data)
23228892ea20SAggelos Economopoulos {
23235da1e9c3SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &rx_data->rx_done;
23245da1e9c3SSepherosa Ziehau 
23258892ea20SAggelos Economopoulos 	while (rx_done->entry[rx_done->idx].length != 0) {
232652cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
232752cf8dfcSSepherosa Ziehau 
23288892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23298892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
233052cf8dfcSSepherosa Ziehau 
23318892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
233252cf8dfcSSepherosa Ziehau 
23335da1e9c3SSepherosa Ziehau 		if (length <= (MHLEN - MXGEFW_PAD)) {
23345da1e9c3SSepherosa Ziehau 			mxge_rx_done_small(ifp, &rx_data->rx_small,
23355da1e9c3SSepherosa Ziehau 			    length, checksum);
23365da1e9c3SSepherosa Ziehau 		} else {
23375da1e9c3SSepherosa Ziehau 			mxge_rx_done_big(ifp, &rx_data->rx_big,
23385da1e9c3SSepherosa Ziehau 			    length, checksum);
23395da1e9c3SSepherosa Ziehau 		}
234052cf8dfcSSepherosa Ziehau 
2341a3f51d6bSSepherosa Ziehau 		rx_done->idx++;
2342a3f51d6bSSepherosa Ziehau 		rx_done->idx &= rx_done->mask;
23438892ea20SAggelos Economopoulos 	}
23448892ea20SAggelos Economopoulos }
23458892ea20SAggelos Economopoulos 
2346ddbf91b7SSepherosa Ziehau static __inline void
23475da1e9c3SSepherosa Ziehau mxge_tx_done(struct ifnet *ifp, mxge_tx_ring_t *tx, uint32_t mcp_idx)
23488892ea20SAggelos Economopoulos {
234926634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
235066e7a0e8SSepherosa Ziehau 
23518892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
235266e7a0e8SSepherosa Ziehau 		struct mbuf *m;
235366e7a0e8SSepherosa Ziehau 		int idx;
235466e7a0e8SSepherosa Ziehau 
23558892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23568892ea20SAggelos Economopoulos 		tx->done++;
235766e7a0e8SSepherosa Ziehau 
23588892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
235966e7a0e8SSepherosa Ziehau 		/*
236066e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
236166e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
236266e7a0e8SSepherosa Ziehau 		 */
23638892ea20SAggelos Economopoulos 		if (m != NULL) {
236448d12a0bSSepherosa Ziehau 			tx->pkt_done++;
2365cc9c62a4SSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
23668892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
236766e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23688892ea20SAggelos Economopoulos 			m_freem(m);
23698892ea20SAggelos Economopoulos 		}
23708892ea20SAggelos Economopoulos 	}
23718892ea20SAggelos Economopoulos 
237266e7a0e8SSepherosa Ziehau 	/*
237366e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
237466e7a0e8SSepherosa Ziehau 	 * its OK to send packets
237566e7a0e8SSepherosa Ziehau 	 */
2376ca8ca004SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 4) {
23779ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
2378ca8ca004SSepherosa Ziehau 		if (tx->req == tx->done)
2379ca8ca004SSepherosa Ziehau 			ifp->if_timer = 0;
2380ca8ca004SSepherosa Ziehau 	}
238189d55360SSepherosa Ziehau 
238289d55360SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
238389d55360SSepherosa Ziehau 		if_devstart(ifp);
238489d55360SSepherosa Ziehau 
23858892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
23868892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
23878892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
23888892ea20SAggelos Economopoulos 		 * are no more transmits pending */
23898892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
23908892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
23918892ea20SAggelos Economopoulos 			tx->queue_active = 0;
23928892ea20SAggelos Economopoulos 			tx->deactivate++;
23938892ea20SAggelos Economopoulos 			wmb();
23948892ea20SAggelos Economopoulos 		}
23958892ea20SAggelos Economopoulos 	}
23968892ea20SAggelos Economopoulos #endif
23978892ea20SAggelos Economopoulos }
23988892ea20SAggelos Economopoulos 
239989d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24008892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24018892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24028892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
24038892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
24048892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
24058892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
24068892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
24078892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
24088892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
24098892ea20SAggelos Economopoulos };
241089d55360SSepherosa Ziehau 
241189d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
241289d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
24138892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
24148892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24158892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
241689d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
241789d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24188892ea20SAggelos Economopoulos };
24198892ea20SAggelos Economopoulos 
24208892ea20SAggelos Economopoulos static void
242189d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24228892ea20SAggelos Economopoulos {
24237cc92483SSepherosa Ziehau 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 0, NULL);
242489d55360SSepherosa Ziehau 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
242589d55360SSepherosa Ziehau 	sc->current_media = media_type;
242689d55360SSepherosa Ziehau 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
24278892ea20SAggelos Economopoulos }
24288892ea20SAggelos Economopoulos 
24298892ea20SAggelos Economopoulos static void
243089d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24318892ea20SAggelos Economopoulos {
2432c7431c78SSepherosa Ziehau 	const char *ptr;
243389d55360SSepherosa Ziehau 	int i;
24348892ea20SAggelos Economopoulos 
243589d55360SSepherosa Ziehau 	ifmedia_removeall(&sc->media);
243689d55360SSepherosa Ziehau 	mxge_media_set(sc, IFM_AUTO);
24378892ea20SAggelos Economopoulos 
24388892ea20SAggelos Economopoulos 	/*
24392f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24408892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24418892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24428892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24438892ea20SAggelos Economopoulos 	 */
24448892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24458892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2446af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
244789d55360SSepherosa Ziehau 		return;
24488892ea20SAggelos Economopoulos 	}
24498892ea20SAggelos Economopoulos 
24508892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
245189d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24528892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2453af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24548892ea20SAggelos Economopoulos 			return;
24558892ea20SAggelos Economopoulos 		}
24568892ea20SAggelos Economopoulos 	}
245789d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24588892ea20SAggelos Economopoulos 		/* -C is CX4 */
245989d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
246089d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
246189d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24628892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
246389d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2464af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24652f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
246689d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
246789d55360SSepherosa Ziehau 		/* -R is XFP */
246889d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
246989d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
247089d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
247189d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
247289d55360SSepherosa Ziehau 	} else {
2473af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
247489d55360SSepherosa Ziehau 	}
24758892ea20SAggelos Economopoulos }
24768892ea20SAggelos Economopoulos 
247789d55360SSepherosa Ziehau /*
247889d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
247989d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
248089d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
248189d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
248289d55360SSepherosa Ziehau  * than in the interrupt handler itself.
248389d55360SSepherosa Ziehau  */
248489d55360SSepherosa Ziehau static void
248589d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
248689d55360SSepherosa Ziehau {
248789d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
24887cc92483SSepherosa Ziehau 	const char *cage_type;
248989d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
249089d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
249189d55360SSepherosa Ziehau 	uint32_t byte;
249289d55360SSepherosa Ziehau 
249389d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
249489d55360SSepherosa Ziehau 
249589d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
24968892ea20SAggelos Economopoulos 		/* -R is XFP */
24978892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
24987cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_xfp_media_types) /
249989d55360SSepherosa Ziehau 		    sizeof(mxge_xfp_media_types[0]);
25008892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25018892ea20SAggelos Economopoulos 		cage_type = "XFP";
250289d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25038892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25048892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
25057cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_sfp_media_types) /
250689d55360SSepherosa Ziehau 		    sizeof(mxge_sfp_media_types[0]);
25078892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25088892ea20SAggelos Economopoulos 		byte = 3;
250989d55360SSepherosa Ziehau 	} else {
251089d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25118892ea20SAggelos Economopoulos 		return;
25128892ea20SAggelos Economopoulos 	}
25138892ea20SAggelos Economopoulos 
25148892ea20SAggelos Economopoulos 	/*
25158892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25168892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25178892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25188892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25198892ea20SAggelos Economopoulos 	 * a millisecond
25208892ea20SAggelos Economopoulos 	 */
25218892ea20SAggelos Economopoulos 
25228892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25238892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25248892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
25257cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2526af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read XFP\n");
25277cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2528af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
25297cc92483SSepherosa Ziehau 	if (err != MXGEFW_CMD_OK)
25308892ea20SAggelos Economopoulos 		return;
25318892ea20SAggelos Economopoulos 
25327cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25338892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25348892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25357cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25368892ea20SAggelos Economopoulos 		DELAY(1000);
25378892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25388892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25398892ea20SAggelos Economopoulos 	}
25408892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2541af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25428892ea20SAggelos Economopoulos 		    cage_type, err, ms);
25438892ea20SAggelos Economopoulos 		return;
25448892ea20SAggelos Economopoulos 	}
25458892ea20SAggelos Economopoulos 
25468892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25477cc92483SSepherosa Ziehau 		if (bootverbose) {
2548af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25498892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25507cc92483SSepherosa Ziehau 		}
255189d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
255289d55360SSepherosa Ziehau 			mxge_media_init(sc);
255389d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
255489d55360SSepherosa Ziehau 		}
25558892ea20SAggelos Economopoulos 		return;
25568892ea20SAggelos Economopoulos 	}
25578892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25588892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25597cc92483SSepherosa Ziehau 			if (bootverbose) {
2560af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25618892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25627cc92483SSepherosa Ziehau 			}
25638892ea20SAggelos Economopoulos 
256489d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
256589d55360SSepherosa Ziehau 				mxge_media_init(sc);
256689d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
256789d55360SSepherosa Ziehau 			}
25688892ea20SAggelos Economopoulos 			return;
25698892ea20SAggelos Economopoulos 		}
25708892ea20SAggelos Economopoulos 	}
25717cc92483SSepherosa Ziehau 	if (bootverbose) {
2572af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
25737cc92483SSepherosa Ziehau 		    cmd.data0);
25747cc92483SSepherosa Ziehau 	}
25758892ea20SAggelos Economopoulos }
25768892ea20SAggelos Economopoulos 
25778892ea20SAggelos Economopoulos static void
2578cf5afd69SSepherosa Ziehau mxge_intr_status(struct mxge_softc *sc, const mcp_irq_data_t *stats)
25798892ea20SAggelos Economopoulos {
25808892ea20SAggelos Economopoulos 	if (sc->link_state != stats->link_up) {
25818892ea20SAggelos Economopoulos 		sc->link_state = stats->link_up;
25828892ea20SAggelos Economopoulos 		if (sc->link_state) {
258373a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_UP;
258473a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
25857cc92483SSepherosa Ziehau 			if (bootverbose)
2586cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link up\n");
25878892ea20SAggelos Economopoulos 		} else {
258873a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
258973a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
25907cc92483SSepherosa Ziehau 			if (bootverbose)
2591cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link down\n");
25928892ea20SAggelos Economopoulos 		}
25938892ea20SAggelos Economopoulos 		sc->need_media_probe = 1;
25948892ea20SAggelos Economopoulos 	}
2595cf5afd69SSepherosa Ziehau 
2596cf5afd69SSepherosa Ziehau 	if (sc->rdma_tags_available != be32toh(stats->rdma_tags_available)) {
2597cf5afd69SSepherosa Ziehau 		sc->rdma_tags_available = be32toh(stats->rdma_tags_available);
2598cf5afd69SSepherosa Ziehau 		if_printf(sc->ifp, "RDMA timed out! %d tags left\n",
2599cf5afd69SSepherosa Ziehau 		    sc->rdma_tags_available);
26008892ea20SAggelos Economopoulos 	}
26018892ea20SAggelos Economopoulos 
26028892ea20SAggelos Economopoulos 	if (stats->link_down) {
26038892ea20SAggelos Economopoulos 		sc->down_cnt += stats->link_down;
26048892ea20SAggelos Economopoulos 		sc->link_state = 0;
2605f0115d64SAggelos Economopoulos 		sc->ifp->if_link_state = LINK_STATE_DOWN;
2606f0115d64SAggelos Economopoulos 		if_link_state_change(sc->ifp);
26078892ea20SAggelos Economopoulos 	}
26088892ea20SAggelos Economopoulos }
26098892ea20SAggelos Economopoulos 
2610cf5afd69SSepherosa Ziehau static void
261126634ef8SSepherosa Ziehau mxge_serialize_skipmain(struct mxge_softc *sc)
261226634ef8SSepherosa Ziehau {
261326634ef8SSepherosa Ziehau 	lwkt_serialize_array_enter(sc->serializes, sc->nserialize, 1);
261426634ef8SSepherosa Ziehau }
261526634ef8SSepherosa Ziehau 
261626634ef8SSepherosa Ziehau static void
261726634ef8SSepherosa Ziehau mxge_deserialize_skipmain(struct mxge_softc *sc)
261826634ef8SSepherosa Ziehau {
261926634ef8SSepherosa Ziehau 	lwkt_serialize_array_exit(sc->serializes, sc->nserialize, 1);
262026634ef8SSepherosa Ziehau }
262126634ef8SSepherosa Ziehau 
262226634ef8SSepherosa Ziehau static void
2623cf5afd69SSepherosa Ziehau mxge_legacy(void *arg)
2624cf5afd69SSepherosa Ziehau {
2625cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2626cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2627cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2628cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26299a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2630cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2631cf5afd69SSepherosa Ziehau 	uint8_t valid;
2632cf5afd69SSepherosa Ziehau 
263326634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
263426634ef8SSepherosa Ziehau 
2635cf5afd69SSepherosa Ziehau #if 0
2636cf5afd69SSepherosa Ziehau 	/* an interrupt on a non-zero slice is implicitly valid
2637cf5afd69SSepherosa Ziehau 	   since MSI-X irqs are not shared */
2638cf5afd69SSepherosa Ziehau 	if (ss != sc->ss) {
2639cf5afd69SSepherosa Ziehau 		mxge_clean_rx_done(rx_done);
2640cf5afd69SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2641cf5afd69SSepherosa Ziehau 		return;
2642cf5afd69SSepherosa Ziehau 	}
2643cf5afd69SSepherosa Ziehau #endif
2644cf5afd69SSepherosa Ziehau 
2645cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2646cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2647cf5afd69SSepherosa Ziehau 		return;
2648cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2649cf5afd69SSepherosa Ziehau 
2650cf5afd69SSepherosa Ziehau 	/* Lower legacy IRQ */
2651cf5afd69SSepherosa Ziehau 	*sc->irq_deassert = 0;
2652cf5afd69SSepherosa Ziehau 	if (!mxge_deassert_wait) {
2653cf5afd69SSepherosa Ziehau 		/* Don't wait for conf. that irq is low */
2654cf5afd69SSepherosa Ziehau 		stats->valid = 0;
2655cf5afd69SSepherosa Ziehau 	}
2656cf5afd69SSepherosa Ziehau 
265726634ef8SSepherosa Ziehau 	mxge_serialize_skipmain(sc);
265826634ef8SSepherosa Ziehau 
2659cf5afd69SSepherosa Ziehau 	/*
2660cf5afd69SSepherosa Ziehau 	 * Loop while waiting for legacy irq deassertion
2661cf5afd69SSepherosa Ziehau 	 * XXX do we really want to loop?
2662cf5afd69SSepherosa Ziehau 	 */
2663cf5afd69SSepherosa Ziehau 	do {
2664cf5afd69SSepherosa Ziehau 		/* Check for transmit completes and receives */
2665cf5afd69SSepherosa Ziehau 		send_done_count = be32toh(stats->send_done_count);
2666cf5afd69SSepherosa Ziehau 		while ((send_done_count != tx->pkt_done) ||
2667cf5afd69SSepherosa Ziehau 		       (rx_done->entry[rx_done->idx].length != 0)) {
26685da1e9c3SSepherosa Ziehau 			if (send_done_count != tx->pkt_done) {
26695da1e9c3SSepherosa Ziehau 				mxge_tx_done(&sc->arpcom.ac_if, tx,
26705da1e9c3SSepherosa Ziehau 				    (int)send_done_count);
26715da1e9c3SSepherosa Ziehau 			}
26725da1e9c3SSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data);
2673cf5afd69SSepherosa Ziehau 			send_done_count = be32toh(stats->send_done_count);
2674cf5afd69SSepherosa Ziehau 		}
2675cf5afd69SSepherosa Ziehau 		if (mxge_deassert_wait)
2676cf5afd69SSepherosa Ziehau 			wmb();
2677cf5afd69SSepherosa Ziehau 	} while (*((volatile uint8_t *)&stats->valid));
2678cf5afd69SSepherosa Ziehau 
267926634ef8SSepherosa Ziehau 	mxge_deserialize_skipmain(sc);
268026634ef8SSepherosa Ziehau 
2681cf5afd69SSepherosa Ziehau 	/* Fw link & error stats meaningful only on the first slice */
2682cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2683cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2684cf5afd69SSepherosa Ziehau 
2685cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
2686cf5afd69SSepherosa Ziehau 	if (valid & 0x1)
2687cf5afd69SSepherosa Ziehau 	    *ss->irq_claim = be32toh(3);
2688cf5afd69SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2689cf5afd69SSepherosa Ziehau }
2690cf5afd69SSepherosa Ziehau 
2691cf5afd69SSepherosa Ziehau static void
2692cf5afd69SSepherosa Ziehau mxge_msi(void *arg)
2693cf5afd69SSepherosa Ziehau {
2694cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2695cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2696cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2697cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26989a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2699cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2700cf5afd69SSepherosa Ziehau 	uint8_t valid;
2701cf5afd69SSepherosa Ziehau 
270226634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
270326634ef8SSepherosa Ziehau 
2704cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2705cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2706cf5afd69SSepherosa Ziehau 		return;
2707cf5afd69SSepherosa Ziehau 
2708cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2709cf5afd69SSepherosa Ziehau 	stats->valid = 0;
2710cf5afd69SSepherosa Ziehau 
2711cf5afd69SSepherosa Ziehau 	/* Check for receives */
271226634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&ss->rx_data.rx_serialize);
2713cf5afd69SSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0)
27145da1e9c3SSepherosa Ziehau 		mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data);
271526634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&ss->rx_data.rx_serialize);
2716cf5afd69SSepherosa Ziehau 
271745bc9b9dSSepherosa Ziehau 	/*
271845bc9b9dSSepherosa Ziehau 	 * Check for transmit completes
271945bc9b9dSSepherosa Ziehau 	 *
272045bc9b9dSSepherosa Ziehau 	 * NOTE:
272145bc9b9dSSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
272245bc9b9dSSepherosa Ziehau 	 * which is called only in interrupt handler, the
272345bc9b9dSSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
272445bc9b9dSSepherosa Ziehau 	 */
2725cf5afd69SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
272645bc9b9dSSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
272745bc9b9dSSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
27285da1e9c3SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
272926634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
273045bc9b9dSSepherosa Ziehau 	}
2731cf5afd69SSepherosa Ziehau 
2732cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2733cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2734cf5afd69SSepherosa Ziehau 
2735cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
27368892ea20SAggelos Economopoulos 	if (valid & 0x1)
27378892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
27388892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
27398892ea20SAggelos Economopoulos }
27408892ea20SAggelos Economopoulos 
27418892ea20SAggelos Economopoulos static void
27428892ea20SAggelos Economopoulos mxge_init(void *arg)
27438892ea20SAggelos Economopoulos {
274489d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
274589d55360SSepherosa Ziehau 
274626634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(sc->ifp);
274789d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
274889d55360SSepherosa Ziehau 		mxge_open(sc);
27498892ea20SAggelos Economopoulos }
27508892ea20SAggelos Economopoulos 
27518892ea20SAggelos Economopoulos static void
27528892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
27538892ea20SAggelos Economopoulos {
27548892ea20SAggelos Economopoulos 	int i;
27558892ea20SAggelos Economopoulos 
27569a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
27579a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.info[i].m == NULL)
27588892ea20SAggelos Economopoulos 			continue;
27599a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_big.dmat,
27609a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map);
27619a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_big.info[i].m);
27629a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info[i].m = NULL;
27638892ea20SAggelos Economopoulos 	}
27648892ea20SAggelos Economopoulos 
27659a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
27669a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.info[i].m == NULL)
27678892ea20SAggelos Economopoulos 			continue;
27689a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_small.dmat,
27699a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map);
27709a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_small.info[i].m);
27719a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info[i].m = NULL;
27728892ea20SAggelos Economopoulos 	}
27738892ea20SAggelos Economopoulos 
27744e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
27758892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
27768892ea20SAggelos Economopoulos 		return;
27778892ea20SAggelos Economopoulos 
27788892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
27798892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
27808892ea20SAggelos Economopoulos 			continue;
27814e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
27828892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
27838892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
27848892ea20SAggelos Economopoulos 	}
27858892ea20SAggelos Economopoulos }
27868892ea20SAggelos Economopoulos 
27878892ea20SAggelos Economopoulos static void
27888892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
27898892ea20SAggelos Economopoulos {
27908892ea20SAggelos Economopoulos 	int slice;
27918892ea20SAggelos Economopoulos 
27928892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
27938892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
27948892ea20SAggelos Economopoulos }
27958892ea20SAggelos Economopoulos 
27968892ea20SAggelos Economopoulos static void
27978892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
27988892ea20SAggelos Economopoulos {
27998892ea20SAggelos Economopoulos 	int i;
28008892ea20SAggelos Economopoulos 
28019a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_done.entry != NULL) {
2802414caf0dSSepherosa Ziehau 		mxge_dma_free(&ss->rx_done_dma);
28039a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = NULL;
2804798c3369SSepherosa Ziehau 	}
28058892ea20SAggelos Economopoulos 
280611868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
280711868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
280811868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2809798c3369SSepherosa Ziehau 	}
28108892ea20SAggelos Economopoulos 
2811798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2812d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
28138892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2814798c3369SSepherosa Ziehau 	}
28158892ea20SAggelos Economopoulos 
28169a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.shadow != NULL) {
28179a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.shadow, M_DEVBUF);
28189a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.shadow = NULL;
2819798c3369SSepherosa Ziehau 	}
28208892ea20SAggelos Economopoulos 
28219a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.shadow != NULL) {
28229a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.shadow, M_DEVBUF);
28239a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow = NULL;
2824798c3369SSepherosa Ziehau 	}
28258892ea20SAggelos Economopoulos 
28268892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
28278892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
28288892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
28298892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
28308892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
28318892ea20SAggelos Economopoulos 			}
28328892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
28338892ea20SAggelos Economopoulos 		}
2834d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
28358892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2836798c3369SSepherosa Ziehau 	}
28378892ea20SAggelos Economopoulos 
28389a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.info != NULL) {
28399a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.dmat != NULL) {
28409a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
28419a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
28429a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[i].map);
28438892ea20SAggelos Economopoulos 			}
28449a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
28459a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
28469a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
28478892ea20SAggelos Economopoulos 		}
28489a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.info, M_DEVBUF);
28499a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info = NULL;
2850798c3369SSepherosa Ziehau 	}
28518892ea20SAggelos Economopoulos 
28529a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.info != NULL) {
28539a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.dmat != NULL) {
28549a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
28559a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
28569a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[i].map);
28578892ea20SAggelos Economopoulos 			}
28589a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
28599a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
28609a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
28618892ea20SAggelos Economopoulos 		}
28629a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.info, M_DEVBUF);
28639a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info = NULL;
28648892ea20SAggelos Economopoulos 	}
2865798c3369SSepherosa Ziehau }
28668892ea20SAggelos Economopoulos 
28678892ea20SAggelos Economopoulos static void
28688892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
28698892ea20SAggelos Economopoulos {
28708892ea20SAggelos Economopoulos 	int slice;
28718892ea20SAggelos Economopoulos 
2872798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2873798c3369SSepherosa Ziehau 		return;
2874798c3369SSepherosa Ziehau 
28758892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
28768892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
28778892ea20SAggelos Economopoulos }
28788892ea20SAggelos Economopoulos 
28798892ea20SAggelos Economopoulos static int
28808892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
28818892ea20SAggelos Economopoulos     int tx_ring_entries)
28828892ea20SAggelos Economopoulos {
28838892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
28848892ea20SAggelos Economopoulos 	size_t bytes;
28858892ea20SAggelos Economopoulos 	int err, i;
28868892ea20SAggelos Economopoulos 
28877cc92483SSepherosa Ziehau 	/*
28887cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
28897cc92483SSepherosa Ziehau 	 */
28908892ea20SAggelos Economopoulos 
28919a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.mask = ss->rx_data.rx_big.mask =
28929a4ae890SSepherosa Ziehau 	    rx_ring_entries - 1;
28939a4ae890SSepherosa Ziehau 	ss->rx_data.rx_done.mask = (2 * rx_ring_entries) - 1;
28948892ea20SAggelos Economopoulos 
28957cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
28969a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.shadow);
28979a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28988892ea20SAggelos Economopoulos 
28999a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.shadow);
29009a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29018892ea20SAggelos Economopoulos 
29027cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
29039a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.info);
29049a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29058892ea20SAggelos Economopoulos 
29069a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.info);
29079a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29088892ea20SAggelos Economopoulos 
29097cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
29108892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29118892ea20SAggelos Economopoulos 				 1,			/* alignment */
29128892ea20SAggelos Economopoulos 				 4096,			/* boundary */
29138892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29148892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29158892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
29168892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
29178892ea20SAggelos Economopoulos 				 1,			/* num segs */
29188892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
29197cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
29207cc92483SSepherosa Ziehau 				 			/* flags */
29219a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_small.dmat); /* tag */
29228892ea20SAggelos Economopoulos 	if (err != 0) {
29238892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
29248892ea20SAggelos Economopoulos 		    err);
29253598cc14SSascha Wildner 		return err;
29268892ea20SAggelos Economopoulos 	}
29278892ea20SAggelos Economopoulos 
29289a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_small.dmat, BUS_DMA_WAITOK,
29299a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_small.extra_map);
2930798c3369SSepherosa Ziehau 	if (err != 0) {
2931798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
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 	}
29369a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
29379a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_small.dmat,
29389a4ae890SSepherosa Ziehau 		    BUS_DMA_WAITOK, &ss->rx_data.rx_small.info[i].map);
2939798c3369SSepherosa Ziehau 		if (err != 0) {
2940798c3369SSepherosa Ziehau 			int j;
2941798c3369SSepherosa Ziehau 
2942798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
2943798c3369SSepherosa Ziehau 
2944798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
29459a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29469a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[j].map);
2947798c3369SSepherosa Ziehau 			}
29489a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29499a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
29509a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29519a4ae890SSepherosa Ziehau 			ss->rx_data.rx_small.dmat = NULL;
2952798c3369SSepherosa Ziehau 			return err;
2953798c3369SSepherosa Ziehau 		}
2954798c3369SSepherosa Ziehau 	}
2955798c3369SSepherosa Ziehau 
29568892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29578892ea20SAggelos Economopoulos 				 1,			/* alignment */
29588892ea20SAggelos Economopoulos 				 4096,			/* boundary */
29598892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29608892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29618892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2962b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
2963b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
29648892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
29657cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
29667cc92483SSepherosa Ziehau 				 			/* flags */
29679a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_big.dmat); /* tag */
29688892ea20SAggelos Economopoulos 	if (err != 0) {
29698892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
29708892ea20SAggelos Economopoulos 		    err);
29713598cc14SSascha Wildner 		return err;
29728892ea20SAggelos Economopoulos 	}
29737cc92483SSepherosa Ziehau 
29749a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
29759a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_big.extra_map);
29768892ea20SAggelos Economopoulos 	if (err != 0) {
29777cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
29789a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29799a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.dmat = NULL;
29803598cc14SSascha Wildner 		return err;
29818892ea20SAggelos Economopoulos 	}
29829a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
29839a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
29849a4ae890SSepherosa Ziehau 		    &ss->rx_data.rx_big.info[i].map);
2985798c3369SSepherosa Ziehau 		if (err != 0) {
2986798c3369SSepherosa Ziehau 			int j;
2987798c3369SSepherosa Ziehau 
2988798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
2989798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
29909a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29919a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[j].map);
2992798c3369SSepherosa Ziehau 			}
29939a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29949a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
29959a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29969a4ae890SSepherosa Ziehau 			ss->rx_data.rx_big.dmat = NULL;
2997798c3369SSepherosa Ziehau 			return err;
2998798c3369SSepherosa Ziehau 		}
2999798c3369SSepherosa Ziehau 	}
30008892ea20SAggelos Economopoulos 
30017cc92483SSepherosa Ziehau 	/*
30027cc92483SSepherosa Ziehau 	 * Now allocate TX resources
30037cc92483SSepherosa Ziehau 	 */
30048892ea20SAggelos Economopoulos 
30058892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30068892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
30078892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
30088892ea20SAggelos Economopoulos 		return 0;
30098892ea20SAggelos Economopoulos #endif
30108892ea20SAggelos Economopoulos 
30118892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
30128892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
30138892ea20SAggelos Economopoulos 
301411868a93SSepherosa Ziehau 	/* Allocate the tx request copy block; MUST be 8 bytes aligned */
301511868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
301611868a93SSepherosa Ziehau 	ss->tx.req_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
301711868a93SSepherosa Ziehau 	/* DragonFly's kmalloc(9) promises at least 8 bytes alignment */
301811868a93SSepherosa Ziehau 	KASSERT(((uintptr_t)ss->tx.req_list & 0x7) == 0,
301911868a93SSepherosa Ziehau 	    ("req_list not 8 bytes aligned"));
30208892ea20SAggelos Economopoulos 
30217cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
30228892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
30237cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
30248892ea20SAggelos Economopoulos 
30257cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
30268892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
3027d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30288892ea20SAggelos Economopoulos 
30297cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
30308892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30318892ea20SAggelos Economopoulos 				 1,			/* alignment */
30328892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
30338892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30348892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30358892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
30367cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
30377cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
30387cc92483SSepherosa Ziehau 				 			/* maxsize */
30398892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
30408892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
30417cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
30427cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
30438892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
30448892ea20SAggelos Economopoulos 	if (err != 0) {
30457cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
30463598cc14SSascha Wildner 		return err;
30478892ea20SAggelos Economopoulos 	}
30488892ea20SAggelos Economopoulos 
30497cc92483SSepherosa Ziehau 	/*
30507cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
30517cc92483SSepherosa Ziehau 	 */
30528892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
30537cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
30547cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
30558892ea20SAggelos Economopoulos 		if (err != 0) {
3056798c3369SSepherosa Ziehau 			int j;
3057798c3369SSepherosa Ziehau 
30587cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
3059798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
3060798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
3061798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
3062798c3369SSepherosa Ziehau 			}
3063798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
3064798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
30653598cc14SSascha Wildner 			return err;
30668892ea20SAggelos Economopoulos 		}
30678892ea20SAggelos Economopoulos 	}
30688892ea20SAggelos Economopoulos 	return 0;
30698892ea20SAggelos Economopoulos }
30708892ea20SAggelos Economopoulos 
30718892ea20SAggelos Economopoulos static int
30728892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
30738892ea20SAggelos Economopoulos {
30748892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30758892ea20SAggelos Economopoulos 	int tx_ring_size;
30768892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
30778892ea20SAggelos Economopoulos 	int err, slice;
30788892ea20SAggelos Economopoulos 
30797cc92483SSepherosa Ziehau 	/* Get ring sizes */
30808892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
30818892ea20SAggelos Economopoulos 	if (err != 0) {
30828892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3083798c3369SSepherosa Ziehau 		return err;
30848892ea20SAggelos Economopoulos 	}
30857cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
30868892ea20SAggelos Economopoulos 
30878892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
30888892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof(mcp_dma_addr_t);
3089f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3090f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
30918892ea20SAggelos Economopoulos 
30928892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
30938892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
30947cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3095798c3369SSepherosa Ziehau 		if (err != 0) {
3096798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3097798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3098798c3369SSepherosa Ziehau 			return err;
3099798c3369SSepherosa Ziehau 		}
31008892ea20SAggelos Economopoulos 	}
31018892ea20SAggelos Economopoulos 	return 0;
31028892ea20SAggelos Economopoulos }
31038892ea20SAggelos Economopoulos 
31048892ea20SAggelos Economopoulos static void
3105b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
31068892ea20SAggelos Economopoulos {
3107b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
31088892ea20SAggelos Economopoulos 
31098892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
31108892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3111b9a8961fSSepherosa Ziehau 	} else {
3112b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
31138892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
31148892ea20SAggelos Economopoulos 	}
31158892ea20SAggelos Economopoulos }
31168892ea20SAggelos Economopoulos 
31178892ea20SAggelos Economopoulos static int
3118b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
31198892ea20SAggelos Economopoulos {
31208892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
31218892ea20SAggelos Economopoulos 	int err, i, slice;
31228892ea20SAggelos Economopoulos 
3123308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
31248892ea20SAggelos Economopoulos 
3125308781adSSepherosa Ziehau 	/*
3126308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3127308781adSSepherosa Ziehau 	 */
31288892ea20SAggelos Economopoulos 	err = 0;
31298892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
31308892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
31318892ea20SAggelos Economopoulos 	if (slice == 0) {
31328892ea20SAggelos Economopoulos #endif
31338892ea20SAggelos Economopoulos 		cmd.data0 = slice;
3134308781adSSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3135308781adSSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3136308781adSSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
31378892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
3138308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
31398892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
3140308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
31418892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
31428892ea20SAggelos Economopoulos 	}
31438892ea20SAggelos Economopoulos #endif
3144308781adSSepherosa Ziehau 
31458892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3146308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
31479a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.lanai =
3148308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3149308781adSSepherosa Ziehau 
31508892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3151308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
31529a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.lanai =
3153308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
31548892ea20SAggelos Economopoulos 
31558892ea20SAggelos Economopoulos 	if (err != 0) {
3156308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
31578892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
31588892ea20SAggelos Economopoulos 		return EIO;
31598892ea20SAggelos Economopoulos 	}
31608892ea20SAggelos Economopoulos 
3161308781adSSepherosa Ziehau 	/*
3162308781adSSepherosa Ziehau 	 * Stock small receive ring
3163308781adSSepherosa Ziehau 	 */
31649a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
31659a4ae890SSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_data.rx_small,
31669a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map, i, TRUE);
31678892ea20SAggelos Economopoulos 		if (err) {
3168308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
31699a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.mask + 1);
31708892ea20SAggelos Economopoulos 			return ENOMEM;
31718892ea20SAggelos Economopoulos 		}
31728892ea20SAggelos Economopoulos 	}
3173308781adSSepherosa Ziehau 
3174308781adSSepherosa Ziehau 	/*
3175308781adSSepherosa Ziehau 	 * Stock big receive ring
3176308781adSSepherosa Ziehau 	 */
31779a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
31789a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_low = 0xffffffff;
31799a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_high = 0xffffffff;
31808892ea20SAggelos Economopoulos 	}
3181308781adSSepherosa Ziehau 
31829a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.cl_size = cl_size;
3183308781adSSepherosa Ziehau 
31849a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
31859a4ae890SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_data.rx_big,
31869a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map, i, TRUE);
31878892ea20SAggelos Economopoulos 		if (err) {
3188308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
31899a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.mask + 1);
31908892ea20SAggelos Economopoulos 			return ENOMEM;
31918892ea20SAggelos Economopoulos 		}
31928892ea20SAggelos Economopoulos 	}
31938892ea20SAggelos Economopoulos 	return 0;
31948892ea20SAggelos Economopoulos }
31958892ea20SAggelos Economopoulos 
31968892ea20SAggelos Economopoulos static int
31978892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
31988892ea20SAggelos Economopoulos {
31996ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
32008892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3201b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
32028892ea20SAggelos Economopoulos 	bus_addr_t bus;
32038892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
32048892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
32058892ea20SAggelos Economopoulos 
320626634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
32076ee6cba3SSepherosa Ziehau 
32088892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
32096ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
32108892ea20SAggelos Economopoulos 
32118892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
32128892ea20SAggelos Economopoulos 	if (err != 0) {
32136ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
32148892ea20SAggelos Economopoulos 		return EIO;
32158892ea20SAggelos Economopoulos 	}
32168892ea20SAggelos Economopoulos 
32178892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
32186ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
32198892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
32206ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
32218892ea20SAggelos Economopoulos 
32226ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
32238892ea20SAggelos Economopoulos 		if (err != 0) {
32246ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
32258892ea20SAggelos Economopoulos 			return err;
32268892ea20SAggelos Economopoulos 		}
32278892ea20SAggelos Economopoulos 
32286ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
32298892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
32308892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
32318892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
32328892ea20SAggelos Economopoulos 
32338892ea20SAggelos Economopoulos 		cmd.data0 = 1;
32346ee6cba3SSepherosa Ziehau 		cmd.data1 = MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
32358892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
32368892ea20SAggelos Economopoulos 		if (err != 0) {
32376ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
32388892ea20SAggelos Economopoulos 			return err;
32398892ea20SAggelos Economopoulos 		}
32408892ea20SAggelos Economopoulos 	}
32418892ea20SAggelos Economopoulos 
324289d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
324389d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
324489d55360SSepherosa Ziehau 	if (err) {
32456ee6cba3SSepherosa Ziehau 		/*
32466ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
32476ee6cba3SSepherosa Ziehau 		 */
32486ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
32496ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
32506ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
32516ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
325289d55360SSepherosa Ziehau 	}
32538892ea20SAggelos Economopoulos 
3254b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
32558892ea20SAggelos Economopoulos 
3256b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
32576ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
32586ee6cba3SSepherosa Ziehau 	/*
32596ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
32606ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
32616ee6cba3SSepherosa Ziehau 	 */
32626ee6cba3SSepherosa Ziehau 
32636ee6cba3SSepherosa Ziehau 	/*
32646ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
32656ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
32662f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
32676ee6cba3SSepherosa Ziehau 	 */
32686ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
32698892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
32706ee6cba3SSepherosa Ziehau 
3271b9a8961fSSepherosa Ziehau 	/* XXX need to cut MXGEFW_PAD here? */
32728892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
32736ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
32746ee6cba3SSepherosa Ziehau 
3275b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
32768892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
32778892ea20SAggelos Economopoulos 
32788892ea20SAggelos Economopoulos 	if (err != 0) {
32796ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32808892ea20SAggelos Economopoulos 		goto abort;
32818892ea20SAggelos Economopoulos 	}
32828892ea20SAggelos Economopoulos 
32838892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
32848892ea20SAggelos Economopoulos 	for (slice = 0;
32858892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
32868892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
32878892ea20SAggelos Economopoulos #else
32888892ea20SAggelos Economopoulos 	     slice < 1;
32898892ea20SAggelos Economopoulos #endif
32908892ea20SAggelos Economopoulos 	     slice++) {
32918892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
32927cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32937cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
32948892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
32958892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
32968892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32978892ea20SAggelos Economopoulos 	}
32988892ea20SAggelos Economopoulos 
32998892ea20SAggelos Economopoulos 	if (err != 0) {
33007cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
33018892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
33028892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
33038892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
33046ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
33058892ea20SAggelos Economopoulos 		    &cmd);
33066ee6cba3SSepherosa Ziehau 
33078892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
33088892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
33098892ea20SAggelos Economopoulos 	} else {
33108892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
33118892ea20SAggelos Economopoulos 	}
33128892ea20SAggelos Economopoulos 
33138892ea20SAggelos Economopoulos 	if (err != 0) {
33146ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
33158892ea20SAggelos Economopoulos 		goto abort;
33168892ea20SAggelos Economopoulos 	}
33178892ea20SAggelos Economopoulos 
33188892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3319b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
33208892ea20SAggelos Economopoulos 		if (err != 0) {
33216ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
33228892ea20SAggelos Economopoulos 			goto abort;
33238892ea20SAggelos Economopoulos 		}
33248892ea20SAggelos Economopoulos 	}
33258892ea20SAggelos Economopoulos 
33268892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
33278892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
33288892ea20SAggelos Economopoulos 	if (err) {
33296ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
33308892ea20SAggelos Economopoulos 		goto abort;
33318892ea20SAggelos Economopoulos 	}
33326ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
33336ee6cba3SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3334ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
33358892ea20SAggelos Economopoulos 
33368892ea20SAggelos Economopoulos 	return 0;
33378892ea20SAggelos Economopoulos 
33388892ea20SAggelos Economopoulos abort:
33398892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33408892ea20SAggelos Economopoulos 	return err;
33418892ea20SAggelos Economopoulos }
33428892ea20SAggelos Economopoulos 
33432c29ffc6SSepherosa Ziehau static void
334489d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
33458892ea20SAggelos Economopoulos {
33462c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
33478892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
33488892ea20SAggelos Economopoulos 	int err, old_down_cnt;
33498892ea20SAggelos Economopoulos 
335026634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
335189d55360SSepherosa Ziehau 
33522c29ffc6SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
33532c29ffc6SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
3354ca8ca004SSepherosa Ziehau 	ifp->if_timer = 0;
33552c29ffc6SSepherosa Ziehau 
335689d55360SSepherosa Ziehau 	if (!down) {
33578892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
33588892ea20SAggelos Economopoulos 		wmb();
33592c29ffc6SSepherosa Ziehau 
33608892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
33612c29ffc6SSepherosa Ziehau 		if (err)
33622c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
33632c29ffc6SSepherosa Ziehau 
33648892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
33652c29ffc6SSepherosa Ziehau 			/* Wait for down irq */
336626634ef8SSepherosa Ziehau 			ifnet_deserialize_all(ifp);
33678892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
336826634ef8SSepherosa Ziehau 			ifnet_serialize_all(ifp);
33698892ea20SAggelos Economopoulos 		}
33702c29ffc6SSepherosa Ziehau 
33718892ea20SAggelos Economopoulos 		wmb();
33722c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
33732c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
337489d55360SSepherosa Ziehau 	}
33758892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
33768892ea20SAggelos Economopoulos }
33778892ea20SAggelos Economopoulos 
33788892ea20SAggelos Economopoulos static void
33798892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
33808892ea20SAggelos Economopoulos {
33818892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33828892ea20SAggelos Economopoulos 	int reg;
338389d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
33848892ea20SAggelos Economopoulos 
33857cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
33868892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
33878892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
33888892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
33898892ea20SAggelos Economopoulos 
339089d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
33918892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
33928892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
33938892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
339489d55360SSepherosa Ziehau 			sc->pectl = pectl;
339589d55360SSepherosa Ziehau 		} else {
33967cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
339789d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
339889d55360SSepherosa Ziehau 		}
33998892ea20SAggelos Economopoulos 	}
34008892ea20SAggelos Economopoulos 
34017cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
34028892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
34038892ea20SAggelos Economopoulos }
34048892ea20SAggelos Economopoulos 
34058892ea20SAggelos Economopoulos static uint32_t
34068892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
34078892ea20SAggelos Economopoulos {
34088892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
34098892ea20SAggelos Economopoulos 	uint32_t vs;
34108892ea20SAggelos Economopoulos 
34118a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
34128892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
34138a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
34148892ea20SAggelos Economopoulos 		return (uint32_t)-1;
34158892ea20SAggelos Economopoulos 	}
34168a20b038SSepherosa Ziehau 	/* Enable read32 mode */
34178892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
34188a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
34198892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
34208a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
34218892ea20SAggelos Economopoulos }
34228892ea20SAggelos Economopoulos 
342389d55360SSepherosa Ziehau static void
342489d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
34258892ea20SAggelos Economopoulos {
34268892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
342789d55360SSepherosa Ziehau 	int err, running;
34288892ea20SAggelos Economopoulos 	uint32_t reboot;
34298892ea20SAggelos Economopoulos 	uint16_t cmd;
34308892ea20SAggelos Economopoulos 
34318892ea20SAggelos Economopoulos 	err = ENXIO;
34328892ea20SAggelos Economopoulos 
34338a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
34348892ea20SAggelos Economopoulos 
34358892ea20SAggelos Economopoulos 	/*
34368a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
34378892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
34388892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
34398892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
34408892ea20SAggelos Economopoulos 	 * again
34418892ea20SAggelos Economopoulos 	 */
34428892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
34438892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
34448892ea20SAggelos Economopoulos 		/*
34458a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
34468892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
34478892ea20SAggelos Economopoulos 		 * back, then give up
34488892ea20SAggelos Economopoulos 		 */
34498892ea20SAggelos Economopoulos 		DELAY(1000*100);
34508892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
34518a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
34528a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
34538892ea20SAggelos Economopoulos 	}
34548892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
34558a20b038SSepherosa Ziehau 		/* Print the reboot status */
34568892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
34578a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
34588a20b038SSepherosa Ziehau 
345989d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
346089d55360SSepherosa Ziehau 		if (running) {
346189d55360SSepherosa Ziehau 			/*
34628a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
346389d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
346489d55360SSepherosa Ziehau 			 */
346589d55360SSepherosa Ziehau 
346689d55360SSepherosa Ziehau 			/* Mark the link as down */
346789d55360SSepherosa Ziehau 			if (sc->link_state) {
346889d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
346989d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
347089d55360SSepherosa Ziehau 			}
347189d55360SSepherosa Ziehau 			mxge_close(sc, 1);
347289d55360SSepherosa Ziehau 		}
34738a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
34748892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
34758892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
34768892ea20SAggelos Economopoulos 
34778a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
34788892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
34798892ea20SAggelos Economopoulos 
34808a20b038SSepherosa Ziehau 		/* Reload f/w */
348189d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
34828a20b038SSepherosa Ziehau 		if (err)
34838a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
34848a20b038SSepherosa Ziehau 		if (running && !err) {
348589d55360SSepherosa Ziehau 			err = mxge_open(sc);
348689d55360SSepherosa Ziehau 			if_devstart_sched(sc->ifp);
348789d55360SSepherosa Ziehau 		}
348889d55360SSepherosa Ziehau 		sc->watchdog_resets++;
348989d55360SSepherosa Ziehau 	} else {
34908a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
349189d55360SSepherosa Ziehau 		err = 0;
349289d55360SSepherosa Ziehau 	}
349389d55360SSepherosa Ziehau 	if (err) {
34948a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
349589d55360SSepherosa Ziehau 	} else {
349689d55360SSepherosa Ziehau 		if (sc->dying == 2)
349789d55360SSepherosa Ziehau 			sc->dying = 0;
349889d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
349989d55360SSepherosa Ziehau 	}
350089d55360SSepherosa Ziehau }
350189d55360SSepherosa Ziehau 
350289d55360SSepherosa Ziehau static void
350389d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
350489d55360SSepherosa Ziehau {
35058a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
35068a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
35078892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
35088a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
35098892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
35108a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
35118a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
35128892ea20SAggelos Economopoulos }
35138892ea20SAggelos Economopoulos 
351489d55360SSepherosa Ziehau static u_long
35158892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
35168892ea20SAggelos Economopoulos {
3517cc9c62a4SSepherosa Ziehau 	u_long ipackets, opackets, pkts;
35188892ea20SAggelos Economopoulos 
3519cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, ipackets);
3520cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, opackets);
352189d55360SSepherosa Ziehau 
3522cc9c62a4SSepherosa Ziehau 	pkts = ipackets - sc->ipackets;
3523cc9c62a4SSepherosa Ziehau 	pkts += opackets - sc->opackets;
352489d55360SSepherosa Ziehau 
3525cc9c62a4SSepherosa Ziehau 	sc->ipackets = ipackets;
3526cc9c62a4SSepherosa Ziehau 	sc->opackets = opackets;
3527cc9c62a4SSepherosa Ziehau 
352889d55360SSepherosa Ziehau 	return pkts;
35298892ea20SAggelos Economopoulos }
35308892ea20SAggelos Economopoulos 
35318892ea20SAggelos Economopoulos static void
35328892ea20SAggelos Economopoulos mxge_tick(void *arg)
35338892ea20SAggelos Economopoulos {
35348892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
353589d55360SSepherosa Ziehau 	u_long pkts = 0;
35368892ea20SAggelos Economopoulos 	int err = 0;
3537ca8ca004SSepherosa Ziehau 	int ticks;
35388892ea20SAggelos Economopoulos 
353926634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&sc->main_serialize);
354089d55360SSepherosa Ziehau 
354189d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3542ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3543ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
354489d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3545ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3546ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
354789d55360SSepherosa Ziehau 	}
354889d55360SSepherosa Ziehau 	if (pkts == 0) {
3549cc9c62a4SSepherosa Ziehau 		uint16_t cmd;
3550cc9c62a4SSepherosa Ziehau 
3551ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
355289d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
355389d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
355489d55360SSepherosa Ziehau 			sc->dying = 2;
355526634ef8SSepherosa Ziehau 			mxge_serialize_skipmain(sc);
355689d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
355726634ef8SSepherosa Ziehau 			mxge_deserialize_skipmain(sc);
355889d55360SSepherosa Ziehau 			err = ENXIO;
355989d55360SSepherosa Ziehau 		}
3560cc9c62a4SSepherosa Ziehau 
3561ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
356289d55360SSepherosa Ziehau 		ticks *= 4;
356389d55360SSepherosa Ziehau 	}
356489d55360SSepherosa Ziehau 
35658892ea20SAggelos Economopoulos 	if (err == 0)
356689d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
356789d55360SSepherosa Ziehau 
356826634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&sc->main_serialize);
35698892ea20SAggelos Economopoulos }
35708892ea20SAggelos Economopoulos 
35718892ea20SAggelos Economopoulos static int
35728892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
35738892ea20SAggelos Economopoulos {
35748892ea20SAggelos Economopoulos 	return EINVAL;
35758892ea20SAggelos Economopoulos }
35768892ea20SAggelos Economopoulos 
35778892ea20SAggelos Economopoulos static int
35788892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
35798892ea20SAggelos Economopoulos {
35808892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
35818892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
35828892ea20SAggelos Economopoulos 	int err = 0;
35838892ea20SAggelos Economopoulos 
3584b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3585b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
35868892ea20SAggelos Economopoulos 		return EINVAL;
3587b9a8961fSSepherosa Ziehau 
35888892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
35898892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
35902ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
359189d55360SSepherosa Ziehau 		mxge_close(sc, 0);
35928892ea20SAggelos Economopoulos 		err = mxge_open(sc);
35938892ea20SAggelos Economopoulos 		if (err != 0) {
35948892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
359589d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3596b9a8961fSSepherosa Ziehau 			mxge_open(sc);
35978892ea20SAggelos Economopoulos 		}
35988892ea20SAggelos Economopoulos 	}
35998892ea20SAggelos Economopoulos 	return err;
36008892ea20SAggelos Economopoulos }
36018892ea20SAggelos Economopoulos 
36028892ea20SAggelos Economopoulos static void
36038892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
36048892ea20SAggelos Economopoulos {
36058892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
36068892ea20SAggelos Economopoulos 
36078892ea20SAggelos Economopoulos 
36088892ea20SAggelos Economopoulos 	if (sc == NULL)
36098892ea20SAggelos Economopoulos 		return;
36108892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
361189d55360SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
36128892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
361389d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
36148892ea20SAggelos Economopoulos }
36158892ea20SAggelos Economopoulos 
36168892ea20SAggelos Economopoulos static int
361789d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
361889d55360SSepherosa Ziehau     struct ucred *cr __unused)
36198892ea20SAggelos Economopoulos {
36208892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
36218892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
36228892ea20SAggelos Economopoulos 	int err, mask;
36238892ea20SAggelos Economopoulos 
362426634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
3625af85d4d5SSepherosa Ziehau 	err = 0;
3626af85d4d5SSepherosa Ziehau 
36278892ea20SAggelos Economopoulos 	switch (command) {
36288892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
36298892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
36308892ea20SAggelos Economopoulos 		break;
36318892ea20SAggelos Economopoulos 
36328892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3633af85d4d5SSepherosa Ziehau 		if (sc->dying)
36348892ea20SAggelos Economopoulos 			return EINVAL;
3635af85d4d5SSepherosa Ziehau 
36368892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
36372ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
36388892ea20SAggelos Economopoulos 				err = mxge_open(sc);
36398892ea20SAggelos Economopoulos 			} else {
3640af85d4d5SSepherosa Ziehau 				/*
3641af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3642af85d4d5SSepherosa Ziehau 				 * flag changes
3643af85d4d5SSepherosa Ziehau 				 */
36448892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
36458892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
36468892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
36478892ea20SAggelos Economopoulos 			}
36488892ea20SAggelos Economopoulos 		} else {
3649af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
365089d55360SSepherosa Ziehau 				mxge_close(sc, 0);
36518892ea20SAggelos Economopoulos 		}
36528892ea20SAggelos Economopoulos 		break;
36538892ea20SAggelos Economopoulos 
36548892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
36558892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
36568892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
36578892ea20SAggelos Economopoulos 		break;
36588892ea20SAggelos Economopoulos 
36598892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
36608892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
36618892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
366289d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
366389d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
366489d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
36658892ea20SAggelos Economopoulos 			else
366689d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
36678892ea20SAggelos Economopoulos 		}
366889d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
366989d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
367089d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
367189d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
367289d55360SSepherosa Ziehau 			else
367389d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
367489d55360SSepherosa Ziehau 		}
367589d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
367689d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
36778892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
36788892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
36798892ea20SAggelos Economopoulos 		break;
36808892ea20SAggelos Economopoulos 
36818892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
368289d55360SSepherosa Ziehau 		mxge_media_probe(sc);
36838892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
36848892ea20SAggelos Economopoulos 		    &sc->media, command);
36858892ea20SAggelos Economopoulos 		break;
36868892ea20SAggelos Economopoulos 
36878892ea20SAggelos Economopoulos 	default:
368889d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
368989d55360SSepherosa Ziehau 		break;
36908892ea20SAggelos Economopoulos 	}
36918892ea20SAggelos Economopoulos 	return err;
36928892ea20SAggelos Economopoulos }
36938892ea20SAggelos Economopoulos 
36948892ea20SAggelos Economopoulos static void
36958892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
36968892ea20SAggelos Economopoulos {
36977cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
36987cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
36997cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
37008892ea20SAggelos Economopoulos 
37017cc92483SSepherosa Ziehau 	/* XXX */
37028892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
37038892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
37047cc92483SSepherosa Ziehau 
37058892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
37068892ea20SAggelos Economopoulos 
370789d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
37087cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
37097cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
37107cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
37117cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
371289d55360SSepherosa Ziehau }
37138892ea20SAggelos Economopoulos 
37148892ea20SAggelos Economopoulos static void
37158892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
37168892ea20SAggelos Economopoulos {
37178892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37188892ea20SAggelos Economopoulos 	int i;
37198892ea20SAggelos Economopoulos 
37208892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
37218892ea20SAggelos Economopoulos 		return;
37228892ea20SAggelos Economopoulos 
37238892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37248892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37258892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
37268892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
37278892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
37288892ea20SAggelos Economopoulos 		}
37299a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_done.entry != NULL) {
3730414caf0dSSepherosa Ziehau 			mxge_dma_free(&ss->rx_done_dma);
37319a4ae890SSepherosa Ziehau 			ss->rx_data.rx_done.entry = NULL;
37328892ea20SAggelos Economopoulos 		}
37338892ea20SAggelos Economopoulos 	}
37346c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
37358892ea20SAggelos Economopoulos 	sc->ss = NULL;
37368892ea20SAggelos Economopoulos }
37378892ea20SAggelos Economopoulos 
37388892ea20SAggelos Economopoulos static int
37398892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
37408892ea20SAggelos Economopoulos {
37418892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
37428892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37438892ea20SAggelos Economopoulos 	size_t bytes;
37448892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
37458892ea20SAggelos Economopoulos 
37468892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37478892ea20SAggelos Economopoulos 	if (err != 0) {
37488892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37498892ea20SAggelos Economopoulos 		return err;
37508892ea20SAggelos Economopoulos 	}
37518892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
37528892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37538892ea20SAggelos Economopoulos 
37548892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
37557cc92483SSepherosa Ziehau 	sc->ss = kmalloc(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
37567cc92483SSepherosa Ziehau 
37578892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37588892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37598892ea20SAggelos Economopoulos 
37608892ea20SAggelos Economopoulos 		ss->sc = sc;
37618892ea20SAggelos Economopoulos 
376226634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->rx_data.rx_serialize);
376326634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->tx.tx_serialize);
376426634ef8SSepherosa Ziehau 
37657cc92483SSepherosa Ziehau 		/*
37667cc92483SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queues
37677cc92483SSepherosa Ziehau 		 */
37689a4ae890SSepherosa Ziehau 		bytes = max_intr_slots * sizeof(*ss->rx_data.rx_done.entry);
3769414caf0dSSepherosa Ziehau 		err = mxge_dma_alloc(sc, &ss->rx_done_dma, bytes, 4096);
3770798c3369SSepherosa Ziehau 		if (err != 0) {
3771798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3772798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3773798c3369SSepherosa Ziehau 			return err;
3774798c3369SSepherosa Ziehau 		}
3775414caf0dSSepherosa Ziehau 		ss->rx_data.rx_done.entry = ss->rx_done_dma.dmem_addr;
37768892ea20SAggelos Economopoulos 
37778892ea20SAggelos Economopoulos 		/*
37787cc92483SSepherosa Ziehau 		 * Allocate the per-slice firmware stats; stats
37798892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
37808892ea20SAggelos Economopoulos 		 * slice for now
37818892ea20SAggelos Economopoulos 		 */
37828892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
37838892ea20SAggelos Economopoulos 		if (i > 0)
37848892ea20SAggelos Economopoulos 			continue;
37858892ea20SAggelos Economopoulos #endif
37868892ea20SAggelos Economopoulos 
37878892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
37888892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37898892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3790798c3369SSepherosa Ziehau 		if (err != 0) {
3791798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3792798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3793798c3369SSepherosa Ziehau 			return err;
3794798c3369SSepherosa Ziehau 		}
37957cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
37968892ea20SAggelos Economopoulos 	}
37977cc92483SSepherosa Ziehau 	return 0;
37988892ea20SAggelos Economopoulos }
37998892ea20SAggelos Economopoulos 
38008892ea20SAggelos Economopoulos static void
38018892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
38028892ea20SAggelos Economopoulos {
38038892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3804c7431c78SSepherosa Ziehau 	const char *old_fw;
38058892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
38068892ea20SAggelos Economopoulos 
38078892ea20SAggelos Economopoulos 	sc->num_slices = 1;
38087cc92483SSepherosa Ziehau 
38098892ea20SAggelos Economopoulos 	/*
38107cc92483SSepherosa Ziehau 	 * XXX
38117cc92483SSepherosa Ziehau 	 *
38127cc92483SSepherosa Ziehau 	 * Don't enable multiple slices if they are not enabled,
38138892ea20SAggelos Economopoulos 	 * or if this is not an SMP system
38148892ea20SAggelos Economopoulos 	 */
3815b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
38168892ea20SAggelos Economopoulos 		return;
38178892ea20SAggelos Economopoulos 
38188892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
38198892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
38208892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
38218892ea20SAggelos Economopoulos 		return;
38228892ea20SAggelos Economopoulos 
38238892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
38248892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
38258892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
38268892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
38278892ea20SAggelos Economopoulos 	else
38288892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
38298892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
38308892ea20SAggelos Economopoulos 	if (status != 0) {
38318892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
38328892ea20SAggelos Economopoulos 		return;
38338892ea20SAggelos Economopoulos 	}
38348892ea20SAggelos Economopoulos 
38358892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
38368892ea20SAggelos Economopoulos 	   is alive */
38378892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
38388892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38398892ea20SAggelos Economopoulos 	if (status != 0) {
38408892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
38418892ea20SAggelos Economopoulos 		goto abort_with_fw;
38428892ea20SAggelos Economopoulos 	}
38438892ea20SAggelos Economopoulos 
38448892ea20SAggelos Economopoulos 	/* get rx ring size */
38458892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38468892ea20SAggelos Economopoulos 	if (status != 0) {
38478892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38488892ea20SAggelos Economopoulos 		goto abort_with_fw;
38498892ea20SAggelos Economopoulos 	}
38508892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38518892ea20SAggelos Economopoulos 
38528892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
38538892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38548892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38558892ea20SAggelos Economopoulos 	if (status != 0) {
38568892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38578892ea20SAggelos Economopoulos 		goto abort_with_fw;
38588892ea20SAggelos Economopoulos 	}
38598892ea20SAggelos Economopoulos 
38608892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
38618892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38628892ea20SAggelos Economopoulos 	if (status != 0) {
38638892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38648892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38658892ea20SAggelos Economopoulos 		goto abort_with_fw;
38668892ea20SAggelos Economopoulos 	}
38678892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
38688892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
38698892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
38708892ea20SAggelos Economopoulos 
38718892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
38728892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
3873ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
3874ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
38758892ea20SAggelos Economopoulos 	} else {
38768892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
38778892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
38788892ea20SAggelos Economopoulos 	}
38798892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
38808892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
38818892ea20SAggelos Economopoulos 		sc->num_slices--;
38828892ea20SAggelos Economopoulos 
38837cc92483SSepherosa Ziehau 	if (bootverbose)
38848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
38858892ea20SAggelos Economopoulos 			      sc->num_slices);
38868892ea20SAggelos Economopoulos 
38878892ea20SAggelos Economopoulos 	return;
38888892ea20SAggelos Economopoulos 
38898892ea20SAggelos Economopoulos abort_with_fw:
38908892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
38918892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
38928892ea20SAggelos Economopoulos }
38938892ea20SAggelos Economopoulos 
3894a26af990SSepherosa Ziehau #if 0
38958892ea20SAggelos Economopoulos static int
38968892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
38978892ea20SAggelos Economopoulos {
38988892ea20SAggelos Economopoulos 	size_t bytes;
38998892ea20SAggelos Economopoulos 	int count, err, i, rid;
39008892ea20SAggelos Economopoulos 
39018892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
39028892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
39038892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
39048892ea20SAggelos Economopoulos 
39058892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
39068892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
39078892ea20SAggelos Economopoulos 		return ENXIO;
39088892ea20SAggelos Economopoulos 	}
39098892ea20SAggelos Economopoulos 
39108892ea20SAggelos Economopoulos 	count = sc->num_slices;
39118892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
39128892ea20SAggelos Economopoulos 	if (err != 0) {
39138892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
39148892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
39158892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
39168892ea20SAggelos Economopoulos 	}
39178892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
39188892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
39198892ea20SAggelos Economopoulos 			      count, sc->num_slices);
39208892ea20SAggelos Economopoulos 		device_printf(sc->dev,
39218892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
39228892ea20SAggelos Economopoulos 			      count);
39238892ea20SAggelos Economopoulos 		err = ENOSPC;
39248892ea20SAggelos Economopoulos 		goto abort_with_msix;
39258892ea20SAggelos Economopoulos 	}
39268892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
3927d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39288892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
39298892ea20SAggelos Economopoulos 		err = ENOMEM;
39308892ea20SAggelos Economopoulos 		goto abort_with_msix;
39318892ea20SAggelos Economopoulos 	}
39328892ea20SAggelos Economopoulos 
39338892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39348892ea20SAggelos Economopoulos 		rid = i + 1;
39358892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39368892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
39378892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
39388892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
39398892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
39408892ea20SAggelos Economopoulos 				      " for message %d\n", i);
39418892ea20SAggelos Economopoulos 			err = ENXIO;
39428892ea20SAggelos Economopoulos 			goto abort_with_res;
39438892ea20SAggelos Economopoulos 		}
39448892ea20SAggelos Economopoulos 	}
39458892ea20SAggelos Economopoulos 
39468892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
3947d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39488892ea20SAggelos Economopoulos 
39498892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39508892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39517d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
39527d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
39532e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
39548892ea20SAggelos Economopoulos 		if (err != 0) {
39558892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
39568892ea20SAggelos Economopoulos 				      "message %d\n", i);
39578892ea20SAggelos Economopoulos 			goto abort_with_intr;
39588892ea20SAggelos Economopoulos 		}
39598892ea20SAggelos Economopoulos 	}
39608892ea20SAggelos Economopoulos 
39617cc92483SSepherosa Ziehau 	if (bootverbose) {
39628892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
39638892ea20SAggelos Economopoulos 			      sc->num_slices);
39648892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
39656c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39666c348da6SAggelos Economopoulos 		kprintf("\n");
39678892ea20SAggelos Economopoulos 	}
39688892ea20SAggelos Economopoulos 	return (0);
39698892ea20SAggelos Economopoulos 
39708892ea20SAggelos Economopoulos abort_with_intr:
39718892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39728892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39738892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39748892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39758892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39768892ea20SAggelos Economopoulos 		}
39778892ea20SAggelos Economopoulos 	}
3978d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39798892ea20SAggelos Economopoulos 
39808892ea20SAggelos Economopoulos 
39818892ea20SAggelos Economopoulos abort_with_res:
39828892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39838892ea20SAggelos Economopoulos 		rid = i + 1;
39848892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39858892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39868892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39878892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39888892ea20SAggelos Economopoulos 	}
3989d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39908892ea20SAggelos Economopoulos 
39918892ea20SAggelos Economopoulos 
39928892ea20SAggelos Economopoulos abort_with_msix:
39938892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39948892ea20SAggelos Economopoulos 
39958892ea20SAggelos Economopoulos abort_with_msix_table:
39968892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39978892ea20SAggelos Economopoulos 			     sc->msix_table_res);
39988892ea20SAggelos Economopoulos 
39998892ea20SAggelos Economopoulos 	return err;
40008892ea20SAggelos Economopoulos }
4001a26af990SSepherosa Ziehau #endif
40028892ea20SAggelos Economopoulos 
40038892ea20SAggelos Economopoulos static int
40048892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
40058892ea20SAggelos Economopoulos {
4006cf5afd69SSepherosa Ziehau 	driver_intr_t *intr_func;
400789d55360SSepherosa Ziehau 	u_int irq_flags;
400851c70c94SSascha Wildner 
40097cc92483SSepherosa Ziehau 	sc->irq_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
40107cc92483SSepherosa Ziehau 	    &sc->irq_rid, &irq_flags);
401189d55360SSepherosa Ziehau 
401289d55360SSepherosa Ziehau 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
401389d55360SSepherosa Ziehau 	    &sc->irq_rid, irq_flags);
40148892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
40158892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
40168892ea20SAggelos Economopoulos 		return ENXIO;
40178892ea20SAggelos Economopoulos 	}
401889d55360SSepherosa Ziehau 
4019cf5afd69SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_LEGACY)
4020cf5afd69SSepherosa Ziehau 		intr_func = mxge_legacy;
4021cf5afd69SSepherosa Ziehau 	else
4022cf5afd69SSepherosa Ziehau 		intr_func = mxge_msi;
4023cf5afd69SSepherosa Ziehau 
4024798c3369SSepherosa Ziehau 	return bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE,
402526634ef8SSepherosa Ziehau 	    intr_func, &sc->ss[0], &sc->ih, &sc->main_serialize);
40268892ea20SAggelos Economopoulos }
40278892ea20SAggelos Economopoulos 
4028a26af990SSepherosa Ziehau #if 0
40298892ea20SAggelos Economopoulos static void
40308892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
40318892ea20SAggelos Economopoulos {
40328892ea20SAggelos Economopoulos 	int i, rid;
40338892ea20SAggelos Economopoulos 
40348892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40358892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
40368892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
40378892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
40388892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
40398892ea20SAggelos Economopoulos 		}
40408892ea20SAggelos Economopoulos 	}
4041d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
40428892ea20SAggelos Economopoulos 
40438892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40448892ea20SAggelos Economopoulos 		rid = i + 1;
40458892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
40468892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40478892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
40488892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
40498892ea20SAggelos Economopoulos 	}
4050d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
40518892ea20SAggelos Economopoulos 
40528892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40538892ea20SAggelos Economopoulos 			     sc->msix_table_res);
40548892ea20SAggelos Economopoulos 
40558892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
40568892ea20SAggelos Economopoulos 	return;
40578892ea20SAggelos Economopoulos }
4058a26af990SSepherosa Ziehau #endif
40598892ea20SAggelos Economopoulos 
40608892ea20SAggelos Economopoulos static int
40618892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
40628892ea20SAggelos Economopoulos {
4063a26af990SSepherosa Ziehau #if 0
40648892ea20SAggelos Economopoulos 	int err;
40658892ea20SAggelos Economopoulos 
40668892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
40678892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40688892ea20SAggelos Economopoulos 	else
40698892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
40708892ea20SAggelos Economopoulos 
40718892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
40728892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
40738892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40748892ea20SAggelos Economopoulos 	}
40758892ea20SAggelos Economopoulos 	return err;
4076a26af990SSepherosa Ziehau #else
4077a26af990SSepherosa Ziehau 	return mxge_add_single_irq(sc);
4078a26af990SSepherosa Ziehau #endif
40798892ea20SAggelos Economopoulos }
40808892ea20SAggelos Economopoulos 
408126634ef8SSepherosa Ziehau static void
408226634ef8SSepherosa Ziehau mxge_setup_serialize(struct mxge_softc *sc)
408326634ef8SSepherosa Ziehau {
408426634ef8SSepherosa Ziehau 	int i = 0, slice;
408526634ef8SSepherosa Ziehau 
408626634ef8SSepherosa Ziehau 	/* Main + rx + tx */
408726634ef8SSepherosa Ziehau 	sc->nserialize = (2 * sc->num_slices) + 1;
408826634ef8SSepherosa Ziehau 	sc->serializes =
408926634ef8SSepherosa Ziehau 	    kmalloc(sc->nserialize * sizeof(struct lwkt_serialize *),
409026634ef8SSepherosa Ziehau 	        M_DEVBUF, M_WAITOK | M_ZERO);
409126634ef8SSepherosa Ziehau 
409226634ef8SSepherosa Ziehau 	/*
409326634ef8SSepherosa Ziehau 	 * Setup serializes
409426634ef8SSepherosa Ziehau 	 *
409526634ef8SSepherosa Ziehau 	 * NOTE: Order is critical
409626634ef8SSepherosa Ziehau 	 */
409726634ef8SSepherosa Ziehau 
409826634ef8SSepherosa Ziehau 	KKASSERT(i < sc->nserialize);
409926634ef8SSepherosa Ziehau 	sc->serializes[i++] = &sc->main_serialize;
410026634ef8SSepherosa Ziehau 
410126634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
410226634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
410326634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].rx_data.rx_serialize;
410426634ef8SSepherosa Ziehau 	}
410526634ef8SSepherosa Ziehau 
410626634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
410726634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
410826634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].tx.tx_serialize;
410926634ef8SSepherosa Ziehau 	}
411026634ef8SSepherosa Ziehau 
411126634ef8SSepherosa Ziehau 	KKASSERT(i == sc->nserialize);
411226634ef8SSepherosa Ziehau }
411326634ef8SSepherosa Ziehau 
411426634ef8SSepherosa Ziehau static void
411526634ef8SSepherosa Ziehau mxge_serialize(struct ifnet *ifp, enum ifnet_serialize slz)
411626634ef8SSepherosa Ziehau {
411726634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
411826634ef8SSepherosa Ziehau 
411926634ef8SSepherosa Ziehau 	ifnet_serialize_array_enter(sc->serializes, sc->nserialize, slz);
412026634ef8SSepherosa Ziehau }
412126634ef8SSepherosa Ziehau 
412226634ef8SSepherosa Ziehau static void
412326634ef8SSepherosa Ziehau mxge_deserialize(struct ifnet *ifp, enum ifnet_serialize slz)
412426634ef8SSepherosa Ziehau {
412526634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
412626634ef8SSepherosa Ziehau 
412726634ef8SSepherosa Ziehau 	ifnet_serialize_array_exit(sc->serializes, sc->nserialize, slz);
412826634ef8SSepherosa Ziehau }
412926634ef8SSepherosa Ziehau 
413026634ef8SSepherosa Ziehau static int
413126634ef8SSepherosa Ziehau mxge_tryserialize(struct ifnet *ifp, enum ifnet_serialize slz)
413226634ef8SSepherosa Ziehau {
413326634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
413426634ef8SSepherosa Ziehau 
413526634ef8SSepherosa Ziehau 	return ifnet_serialize_array_try(sc->serializes, sc->nserialize, slz);
413626634ef8SSepherosa Ziehau }
413726634ef8SSepherosa Ziehau 
413826634ef8SSepherosa Ziehau #ifdef INVARIANTS
413926634ef8SSepherosa Ziehau 
414026634ef8SSepherosa Ziehau static void
414126634ef8SSepherosa Ziehau mxge_serialize_assert(struct ifnet *ifp, enum ifnet_serialize slz,
414226634ef8SSepherosa Ziehau     boolean_t serialized)
414326634ef8SSepherosa Ziehau {
414426634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
414526634ef8SSepherosa Ziehau 
414626634ef8SSepherosa Ziehau 	ifnet_serialize_array_assert(sc->serializes, sc->nserialize,
414726634ef8SSepherosa Ziehau 	    slz, serialized);
414826634ef8SSepherosa Ziehau }
414926634ef8SSepherosa Ziehau 
415026634ef8SSepherosa Ziehau #endif	/* INVARIANTS */
415126634ef8SSepherosa Ziehau 
41528892ea20SAggelos Economopoulos static int
41538892ea20SAggelos Economopoulos mxge_attach(device_t dev)
41548892ea20SAggelos Economopoulos {
41558892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4156137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
41578892ea20SAggelos Economopoulos 	int err, rid;
41588892ea20SAggelos Economopoulos 
4159f0115d64SAggelos Economopoulos 	/*
41607cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4161f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4162f0115d64SAggelos Economopoulos 	 */
4163f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
41648892ea20SAggelos Economopoulos 	sc->dev = dev;
41657cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
4166798c3369SSepherosa Ziehau 	ifmedia_init(&sc->media, 0, mxge_media_change, mxge_media_status);
41677cc92483SSepherosa Ziehau 
416826634ef8SSepherosa Ziehau 	lwkt_serialize_init(&sc->main_serialize);
416926634ef8SSepherosa Ziehau 
41708892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
41718892ea20SAggelos Economopoulos 
41728892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
41738892ea20SAggelos Economopoulos 				 1,			/* alignment */
41748892ea20SAggelos Economopoulos 				 0,			/* boundary */
41758892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
41768892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
41778892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
41787cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
41797cc92483SSepherosa Ziehau 				 0, 			/* num segs */
41807cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
41818892ea20SAggelos Economopoulos 				 0,			/* flags */
41828892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
41838892ea20SAggelos Economopoulos 	if (err != 0) {
4184798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4185798c3369SSepherosa Ziehau 		goto failed;
41868892ea20SAggelos Economopoulos 	}
41878892ea20SAggelos Economopoulos 
4188e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
41898892ea20SAggelos Economopoulos 
41908892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
41918892ea20SAggelos Economopoulos 
41927cc92483SSepherosa Ziehau 	/*
41937cc92483SSepherosa Ziehau 	 * Map the board into the kernel
41947cc92483SSepherosa Ziehau 	 */
41958892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
41967cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
41977cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
41988892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
41998892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
42008892ea20SAggelos Economopoulos 		err = ENXIO;
4201798c3369SSepherosa Ziehau 		goto failed;
42028892ea20SAggelos Economopoulos 	}
42037cc92483SSepherosa Ziehau 
42048892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
42058892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
42068892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
42078892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
42088892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
42098892ea20SAggelos Economopoulos 		err = ENXIO;
4210798c3369SSepherosa Ziehau 		goto failed;
42118892ea20SAggelos Economopoulos 	}
42128892ea20SAggelos Economopoulos 
42137cc92483SSepherosa Ziehau 	/*
42147cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
42157cc92483SSepherosa Ziehau 	 * lanai SRAM
42167cc92483SSepherosa Ziehau 	 */
42178892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
42188892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
42198892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
42208892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
42217cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
42228892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4223798c3369SSepherosa Ziehau 	if (err != 0) {
4224798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4225798c3369SSepherosa Ziehau 		goto failed;
4226798c3369SSepherosa Ziehau 	}
42278892ea20SAggelos Economopoulos 
42287cc92483SSepherosa Ziehau 	/*
42297cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
42307cc92483SSepherosa Ziehau 	 */
42318892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
42328892ea20SAggelos Economopoulos 
42337cc92483SSepherosa Ziehau 	/*
42347cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
42357cc92483SSepherosa Ziehau 	 */
42367cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4237798c3369SSepherosa Ziehau 	if (err != 0) {
4238798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4239798c3369SSepherosa Ziehau 		goto failed;
4240798c3369SSepherosa Ziehau 	}
42417cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
42427cc92483SSepherosa Ziehau 
42438892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4244798c3369SSepherosa Ziehau 	if (err != 0) {
4245798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4246798c3369SSepherosa Ziehau 		goto failed;
4247798c3369SSepherosa Ziehau 	}
42488892ea20SAggelos Economopoulos 
42498892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4250798c3369SSepherosa Ziehau 	if (err != 0) {
4251798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4252798c3369SSepherosa Ziehau 		goto failed;
4253798c3369SSepherosa Ziehau 	}
42548892ea20SAggelos Economopoulos 
42557cc92483SSepherosa Ziehau 	/* Select & load the firmware */
42568892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4257798c3369SSepherosa Ziehau 	if (err != 0) {
4258798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4259798c3369SSepherosa Ziehau 		goto failed;
4260798c3369SSepherosa Ziehau 	}
42618892ea20SAggelos Economopoulos 
42628892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
42638892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4264798c3369SSepherosa Ziehau 	if (err != 0) {
4265798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4266798c3369SSepherosa Ziehau 		goto failed;
4267798c3369SSepherosa Ziehau 	}
42688892ea20SAggelos Economopoulos 
426926634ef8SSepherosa Ziehau 	/* Setup serializes */
427026634ef8SSepherosa Ziehau 	mxge_setup_serialize(sc);
427126634ef8SSepherosa Ziehau 
42728892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4273798c3369SSepherosa Ziehau 	if (err != 0) {
4274798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4275798c3369SSepherosa Ziehau 		goto failed;
4276798c3369SSepherosa Ziehau 	}
42778892ea20SAggelos Economopoulos 
42788892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
42798892ea20SAggelos Economopoulos 	if (err != 0) {
4280798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4281798c3369SSepherosa Ziehau 		goto failed;
42828892ea20SAggelos Economopoulos 	}
42838892ea20SAggelos Economopoulos 
42848892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
428589d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
42868892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
428789d55360SSepherosa Ziehau 
428889d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
428989d55360SSepherosa Ziehau #if 0
429089d55360SSepherosa Ziehau 	/* Well, its software, sigh */
429189d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
429289d55360SSepherosa Ziehau #endif
42938892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
429489d55360SSepherosa Ziehau 
42958892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
42968892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
429789d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
42988892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
42998892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
4300ca8ca004SSepherosa Ziehau 	ifp->if_watchdog = mxge_watchdog;
430126634ef8SSepherosa Ziehau 	ifp->if_serialize = mxge_serialize;
430226634ef8SSepherosa Ziehau 	ifp->if_deserialize = mxge_deserialize;
430326634ef8SSepherosa Ziehau 	ifp->if_tryserialize = mxge_tryserialize;
430426634ef8SSepherosa Ziehau #ifdef INVARIANTS
430526634ef8SSepherosa Ziehau 	ifp->if_serialize_assert = mxge_serialize_assert;
430626634ef8SSepherosa Ziehau #endif
430789d55360SSepherosa Ziehau 
4308820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4309820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4310820e213fSSepherosa Ziehau 
43118892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
431289d55360SSepherosa Ziehau 	mxge_media_init(sc);
43138892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
431489d55360SSepherosa Ziehau 
4315cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
431689d55360SSepherosa Ziehau 
4317b9a8961fSSepherosa Ziehau 	/*
4318b9a8961fSSepherosa Ziehau 	 * XXX
4319b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4320b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4321b9a8961fSSepherosa Ziehau 	 */
4322b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4323b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
432489d55360SSepherosa Ziehau 	sc->dying = 0;
432589d55360SSepherosa Ziehau 
4326369c353eSAggelos Economopoulos 	/* must come after ether_ifattach() */
4327369c353eSAggelos Economopoulos 	err = mxge_add_irq(sc);
4328369c353eSAggelos Economopoulos 	if (err != 0) {
4329798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4330798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4331798c3369SSepherosa Ziehau 		goto failed;
4332369c353eSAggelos Economopoulos 	}
433326634ef8SSepherosa Ziehau 
433489d55360SSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->irq_res));
433526634ef8SSepherosa Ziehau 	ifq_set_hw_serialize(&ifp->if_snd, &sc->ss[0].tx.tx_serialize);
43368892ea20SAggelos Economopoulos 
43378892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
433889d55360SSepherosa Ziehau 
4339c9317e74SSepherosa Ziehau 	callout_reset_bycpu(&sc->co_hdl, mxge_ticks, mxge_tick, sc,
4340c9317e74SSepherosa Ziehau 	    rman_get_cpuid(sc->irq_res));
43418892ea20SAggelos Economopoulos 	return 0;
43428892ea20SAggelos Economopoulos 
4343798c3369SSepherosa Ziehau failed:
4344798c3369SSepherosa Ziehau 	mxge_detach(dev);
43458892ea20SAggelos Economopoulos 	return err;
43468892ea20SAggelos Economopoulos }
43478892ea20SAggelos Economopoulos 
43488892ea20SAggelos Economopoulos static int
43498892ea20SAggelos Economopoulos mxge_detach(device_t dev)
43508892ea20SAggelos Economopoulos {
43518892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
43528892ea20SAggelos Economopoulos 
4353798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4354798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
4355798c3369SSepherosa Ziehau 
435626634ef8SSepherosa Ziehau 		ifnet_serialize_all(ifp);
4357798c3369SSepherosa Ziehau 
43588892ea20SAggelos Economopoulos 		sc->dying = 1;
4359798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
436089d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4361e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4362798c3369SSepherosa Ziehau 
4363798c3369SSepherosa Ziehau 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
4364798c3369SSepherosa Ziehau 
436526634ef8SSepherosa Ziehau 		ifnet_deserialize_all(ifp);
4366e3dc37faSAggelos Economopoulos 
436789d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
436889d55360SSepherosa Ziehau 
4369798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4370798c3369SSepherosa Ziehau 	}
43718892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4372798c3369SSepherosa Ziehau 
4373798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4374798c3369SSepherosa Ziehau 	    sc->sram != NULL)
43758892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4376798c3369SSepherosa Ziehau 
43778892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
43788892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4379798c3369SSepherosa Ziehau 
4380798c3369SSepherosa Ziehau 	/* MUST after sysctls and rings are freed */
43818892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4382798c3369SSepherosa Ziehau 
4383798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
43848892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4385798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
43868892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4387798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
43888892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4389798c3369SSepherosa Ziehau 
4390798c3369SSepherosa Ziehau 	if (sc->irq_res != NULL) {
4391798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
4392798c3369SSepherosa Ziehau 		    sc->irq_res);
4393798c3369SSepherosa Ziehau 	}
4394798c3369SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_MSI)
4395798c3369SSepherosa Ziehau 		pci_release_msi(dev);
4396798c3369SSepherosa Ziehau 
4397798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4398798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4399798c3369SSepherosa Ziehau 		    sc->mem_res);
4400798c3369SSepherosa Ziehau 	}
4401798c3369SSepherosa Ziehau 
4402798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
44038892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4404798c3369SSepherosa Ziehau 
44058892ea20SAggelos Economopoulos 	return 0;
44068892ea20SAggelos Economopoulos }
44078892ea20SAggelos Economopoulos 
44088892ea20SAggelos Economopoulos static int
44098892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
44108892ea20SAggelos Economopoulos {
44118892ea20SAggelos Economopoulos 	return 0;
44128892ea20SAggelos Economopoulos }
4413