xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision 4e5bf8bd)
18892ea20SAggelos Economopoulos /******************************************************************************
28892ea20SAggelos Economopoulos 
389d55360SSepherosa Ziehau Copyright (c) 2006-2013, Myricom Inc.
48892ea20SAggelos Economopoulos All rights reserved.
58892ea20SAggelos Economopoulos 
68892ea20SAggelos Economopoulos Redistribution and use in source and binary forms, with or without
78892ea20SAggelos Economopoulos modification, are permitted provided that the following conditions are met:
88892ea20SAggelos Economopoulos 
98892ea20SAggelos Economopoulos  1. Redistributions of source code must retain the above copyright notice,
108892ea20SAggelos Economopoulos     this list of conditions and the following disclaimer.
118892ea20SAggelos Economopoulos 
128892ea20SAggelos Economopoulos  2. Neither the name of the Myricom Inc, nor the names of its
138892ea20SAggelos Economopoulos     contributors may be used to endorse or promote products derived from
148892ea20SAggelos Economopoulos     this software without specific prior written permission.
158892ea20SAggelos Economopoulos 
168892ea20SAggelos Economopoulos THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
178892ea20SAggelos Economopoulos AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188892ea20SAggelos Economopoulos IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198892ea20SAggelos Economopoulos ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
208892ea20SAggelos Economopoulos LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
218892ea20SAggelos Economopoulos CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
228892ea20SAggelos Economopoulos SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
238892ea20SAggelos Economopoulos INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
248892ea20SAggelos Economopoulos CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
258892ea20SAggelos Economopoulos ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
268892ea20SAggelos Economopoulos POSSIBILITY OF SUCH DAMAGE.
278892ea20SAggelos Economopoulos 
2889d55360SSepherosa Ziehau $FreeBSD: head/sys/dev/mxge/if_mxge.c 254263 2013-08-12 23:30:01Z scottl $
298892ea20SAggelos Economopoulos 
3032af04f7SSascha Wildner ***************************************************************************/
318892ea20SAggelos Economopoulos 
3289d55360SSepherosa Ziehau #include "opt_inet.h"
3389d55360SSepherosa Ziehau 
348892ea20SAggelos Economopoulos #include <sys/param.h>
358892ea20SAggelos Economopoulos #include <sys/systm.h>
368892ea20SAggelos Economopoulos #include <sys/linker.h>
378892ea20SAggelos Economopoulos #include <sys/firmware.h>
388892ea20SAggelos Economopoulos #include <sys/endian.h>
3905e71c89SAggelos Economopoulos #include <sys/in_cksum.h>
408892ea20SAggelos Economopoulos #include <sys/sockio.h>
418892ea20SAggelos Economopoulos #include <sys/mbuf.h>
428892ea20SAggelos Economopoulos #include <sys/malloc.h>
438892ea20SAggelos Economopoulos #include <sys/kernel.h>
448892ea20SAggelos Economopoulos #include <sys/module.h>
452e8181d0SAggelos Economopoulos #include <sys/serialize.h>
468892ea20SAggelos Economopoulos #include <sys/socket.h>
478892ea20SAggelos Economopoulos #include <sys/sysctl.h>
488892ea20SAggelos Economopoulos 
498892ea20SAggelos Economopoulos #include <net/if.h>
508892ea20SAggelos Economopoulos #include <net/if_arp.h>
51f2f758dfSAggelos Economopoulos #include <net/ifq_var.h>
528892ea20SAggelos Economopoulos #include <net/ethernet.h>
538892ea20SAggelos Economopoulos #include <net/if_dl.h>
548892ea20SAggelos Economopoulos #include <net/if_media.h>
558892ea20SAggelos Economopoulos 
568892ea20SAggelos Economopoulos #include <net/bpf.h>
578892ea20SAggelos Economopoulos 
588892ea20SAggelos Economopoulos #include <net/if_types.h>
59b3535a6fSAggelos Economopoulos #include <net/vlan/if_vlan_var.h>
608892ea20SAggelos Economopoulos #include <net/zlib.h>
618892ea20SAggelos Economopoulos 
628892ea20SAggelos Economopoulos #include <netinet/in_systm.h>
638892ea20SAggelos Economopoulos #include <netinet/in.h>
648892ea20SAggelos Economopoulos #include <netinet/ip.h>
658892ea20SAggelos Economopoulos #include <netinet/tcp.h>
668892ea20SAggelos Economopoulos 
678892ea20SAggelos Economopoulos #include <sys/bus.h>
688892ea20SAggelos Economopoulos #include <sys/rman.h>
698892ea20SAggelos Economopoulos 
70b3535a6fSAggelos Economopoulos #include <bus/pci/pcireg.h>
71b3535a6fSAggelos Economopoulos #include <bus/pci/pcivar.h>
72b3535a6fSAggelos Economopoulos #include <bus/pci/pci_private.h> /* XXX for pci_cfg_restore */
738892ea20SAggelos Economopoulos 
748892ea20SAggelos Economopoulos #include <vm/vm.h>		/* for pmap_mapdev() */
758892ea20SAggelos Economopoulos #include <vm/pmap.h>
768892ea20SAggelos Economopoulos 
7789d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
788892ea20SAggelos Economopoulos #include <machine/specialreg.h>
798892ea20SAggelos Economopoulos #endif
808892ea20SAggelos Economopoulos 
81b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mxge_mcp.h>
82b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mcp_gen_header.h>
83b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/if_mxge_var.h>
848892ea20SAggelos Economopoulos 
858892ea20SAggelos Economopoulos /* tunable params */
868892ea20SAggelos Economopoulos static int mxge_nvidia_ecrc_enable = 1;
878892ea20SAggelos Economopoulos static int mxge_force_firmware = 0;
887cc92483SSepherosa Ziehau static int mxge_intr_coal_delay = MXGE_INTR_COAL_DELAY;
898892ea20SAggelos Economopoulos static int mxge_deassert_wait = 1;
908892ea20SAggelos Economopoulos static int mxge_flow_control = 1;
918892ea20SAggelos Economopoulos static int mxge_ticks;
928892ea20SAggelos Economopoulos static int mxge_max_slices = 1;
938892ea20SAggelos Economopoulos static int mxge_always_promisc = 0;
9489d55360SSepherosa Ziehau static int mxge_throttle = 0;
957cc92483SSepherosa Ziehau static int mxge_msi_enable = 1;
967cc92483SSepherosa Ziehau 
97c7431c78SSepherosa Ziehau static const char *mxge_fw_unaligned = "mxge_ethp_z8e";
98c7431c78SSepherosa Ziehau static const char *mxge_fw_aligned = "mxge_eth_z8e";
99c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
100c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
1018892ea20SAggelos Economopoulos 
1027cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.max_slices", &mxge_max_slices);
1037cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.flow_control_enabled", &mxge_flow_control);
1047cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.intr_coal_delay", &mxge_intr_coal_delay);
1057cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.nvidia_ecrc_enable", &mxge_nvidia_ecrc_enable);
1067cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.force_firmware", &mxge_force_firmware);
1077cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.deassert_wait", &mxge_deassert_wait);
1087cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.ticks", &mxge_ticks);
1097cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.always_promisc", &mxge_always_promisc);
1107cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.throttle", &mxge_throttle);
1117cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.msi.enable", &mxge_msi_enable);
1127cc92483SSepherosa Ziehau 
1138892ea20SAggelos Economopoulos static int mxge_probe(device_t dev);
1148892ea20SAggelos Economopoulos static int mxge_attach(device_t dev);
1158892ea20SAggelos Economopoulos static int mxge_detach(device_t dev);
1168892ea20SAggelos Economopoulos static int mxge_shutdown(device_t dev);
1178892ea20SAggelos Economopoulos static void mxge_intr(void *arg);
1188892ea20SAggelos Economopoulos 
11989d55360SSepherosa Ziehau static device_method_t mxge_methods[] = {
1208892ea20SAggelos Economopoulos 	/* Device interface */
1218892ea20SAggelos Economopoulos 	DEVMETHOD(device_probe, mxge_probe),
1228892ea20SAggelos Economopoulos 	DEVMETHOD(device_attach, mxge_attach),
1238892ea20SAggelos Economopoulos 	DEVMETHOD(device_detach, mxge_detach),
1248892ea20SAggelos Economopoulos 	DEVMETHOD(device_shutdown, mxge_shutdown),
125d3c9c58eSSascha Wildner 	DEVMETHOD_END
1268892ea20SAggelos Economopoulos };
1278892ea20SAggelos Economopoulos 
12889d55360SSepherosa Ziehau static driver_t mxge_driver = {
1298892ea20SAggelos Economopoulos 	"mxge",
1308892ea20SAggelos Economopoulos 	mxge_methods,
1318892ea20SAggelos Economopoulos 	sizeof(mxge_softc_t),
1328892ea20SAggelos Economopoulos };
1338892ea20SAggelos Economopoulos 
1348892ea20SAggelos Economopoulos static devclass_t mxge_devclass;
1358892ea20SAggelos Economopoulos 
1368892ea20SAggelos Economopoulos /* Declare ourselves to be a child of the PCI bus.*/
137aa2b9d05SSascha Wildner DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, NULL, NULL);
1388892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, firmware, 1, 1, 1);
1398892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, zlib, 1, 1, 1);
1408892ea20SAggelos Economopoulos 
1418892ea20SAggelos Economopoulos static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1428892ea20SAggelos Economopoulos static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1432c29ffc6SSepherosa Ziehau static void mxge_close(mxge_softc_t *sc, int down);
1448892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1458892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
1468892ea20SAggelos Economopoulos 
1478892ea20SAggelos Economopoulos static int
1488892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1498892ea20SAggelos Economopoulos {
15024e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
15124e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
15224e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
15324e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1548892ea20SAggelos Economopoulos 
1558892ea20SAggelos Economopoulos 		switch (rev) {
1568892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1578892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1588892ea20SAggelos Economopoulos 			break;
1598892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1608892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1618892ea20SAggelos Economopoulos 			break;
1628892ea20SAggelos Economopoulos 		default:
1638892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
16424e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1658892ea20SAggelos Economopoulos 			break;
1668892ea20SAggelos Economopoulos 		}
1678892ea20SAggelos Economopoulos 		return 0;
1688892ea20SAggelos Economopoulos 	}
1698892ea20SAggelos Economopoulos 	return ENXIO;
1708892ea20SAggelos Economopoulos }
1718892ea20SAggelos Economopoulos 
1728892ea20SAggelos Economopoulos static void
1738892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1748892ea20SAggelos Economopoulos {
17589d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
1768892ea20SAggelos Economopoulos 	vm_offset_t len;
1778892ea20SAggelos Economopoulos 
1788892ea20SAggelos Economopoulos 	sc->wc = 1;
1798892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
18089d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
18189d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
1829eb279beSAggelos Economopoulos #endif
1838892ea20SAggelos Economopoulos }
1848892ea20SAggelos Economopoulos 
1858892ea20SAggelos Economopoulos static int
1867cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
1878892ea20SAggelos Economopoulos     bus_size_t alignment)
1888892ea20SAggelos Economopoulos {
1897cc92483SSepherosa Ziehau 	bus_size_t boundary;
1908892ea20SAggelos Economopoulos 	int err;
1918892ea20SAggelos Economopoulos 
1927cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
1938892ea20SAggelos Economopoulos 		boundary = 0;
1947cc92483SSepherosa Ziehau 	else
1958892ea20SAggelos Economopoulos 		boundary = 4096;
1968892ea20SAggelos Economopoulos 
1977cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
1987cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
1997cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2008892ea20SAggelos Economopoulos 	if (err != 0) {
2017cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2028892ea20SAggelos Economopoulos 		return err;
2038892ea20SAggelos Economopoulos 	}
2048892ea20SAggelos Economopoulos 	return 0;
2058892ea20SAggelos Economopoulos }
2068892ea20SAggelos Economopoulos 
2078892ea20SAggelos Economopoulos static void
2087cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2098892ea20SAggelos Economopoulos {
2107cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2117cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2127cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2138892ea20SAggelos Economopoulos }
2148892ea20SAggelos Economopoulos 
2158892ea20SAggelos Economopoulos /*
2168892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2178892ea20SAggelos Economopoulos  * SN=x\0
2188892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2198892ea20SAggelos Economopoulos  * PC=text\0
2208892ea20SAggelos Economopoulos  */
2218892ea20SAggelos Economopoulos static int
2228892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2238892ea20SAggelos Economopoulos {
224c7431c78SSepherosa Ziehau 	const char *ptr;
22589d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
22689d55360SSepherosa Ziehau 	char *endptr;
2278892ea20SAggelos Economopoulos 
2288892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2298892ea20SAggelos Economopoulos 	found_mac = 0;
23089d55360SSepherosa Ziehau 	found_sn2 = 0;
23189d55360SSepherosa Ziehau 	while (*ptr != '\0') {
23289d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
23389d55360SSepherosa Ziehau 			ptr += 4;
23489d55360SSepherosa Ziehau 			for (i = 0;;) {
23589d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
23689d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2378892ea20SAggelos Economopoulos 					goto abort;
23889d55360SSepherosa Ziehau 				ptr = endptr;
23989d55360SSepherosa Ziehau 				if (++i == 6)
24089d55360SSepherosa Ziehau 					break;
24189d55360SSepherosa Ziehau 				if (*ptr++ != ':')
24289d55360SSepherosa Ziehau 					goto abort;
24389d55360SSepherosa Ziehau 			}
2448892ea20SAggelos Economopoulos 			found_mac = 1;
24589d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2468892ea20SAggelos Economopoulos 			ptr += 3;
24789d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
24889d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
24989d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2508892ea20SAggelos Economopoulos 			ptr += 3;
25189d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25289d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
25389d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
25489d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
25589d55360SSepherosa Ziehau 			ptr += 4;
25689d55360SSepherosa Ziehau 			found_sn2 = 1;
25789d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
25889d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2598892ea20SAggelos Economopoulos 		}
26089d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2618892ea20SAggelos Economopoulos 	}
2628892ea20SAggelos Economopoulos 
2638892ea20SAggelos Economopoulos 	if (found_mac)
2648892ea20SAggelos Economopoulos 		return 0;
2658892ea20SAggelos Economopoulos 
2668892ea20SAggelos Economopoulos abort:
2678892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2688892ea20SAggelos Economopoulos 	return ENXIO;
2698892ea20SAggelos Economopoulos }
2708892ea20SAggelos Economopoulos 
27189d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
27289d55360SSepherosa Ziehau 
2738892ea20SAggelos Economopoulos static void
2748892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
2758892ea20SAggelos Economopoulos {
2768892ea20SAggelos Economopoulos 	uint32_t val;
2778892ea20SAggelos Economopoulos 	unsigned long base, off;
2788892ea20SAggelos Economopoulos 	char *va, *cfgptr;
2798892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
2808892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
2818892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
2828892ea20SAggelos Economopoulos 	uint32_t *ptr32;
2838892ea20SAggelos Economopoulos 
2848892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
2858892ea20SAggelos Economopoulos 		return;
2868892ea20SAggelos Economopoulos 
2878892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
2888892ea20SAggelos Economopoulos 	if (pdev == NULL) {
2898892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
2908892ea20SAggelos Economopoulos 		return;
2918892ea20SAggelos Economopoulos 	}
2928892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
2938892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
2948892ea20SAggelos Economopoulos 
2958892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
2968892ea20SAggelos Economopoulos 		return;
2978892ea20SAggelos Economopoulos 
2988892ea20SAggelos Economopoulos 	base = 0;
2998892ea20SAggelos Economopoulos 
3008892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3018892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3028892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3038892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3048892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3058892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3068892ea20SAggelos Economopoulos 		if (mcp55 &&
3078892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3088892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3098892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3108892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3118892ea20SAggelos Economopoulos 		}
3128892ea20SAggelos Economopoulos 	}
3138892ea20SAggelos Economopoulos 	if (!base)
3148892ea20SAggelos Economopoulos 		return;
3158892ea20SAggelos Economopoulos 
3167cc92483SSepherosa Ziehau 	/*
3177cc92483SSepherosa Ziehau 	 * XXXX
3187cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3197cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3207cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3217cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3227cc92483SSepherosa Ziehau 	 * FreeBSD grows support for extended pcie config space access
3238892ea20SAggelos Economopoulos 	 */
3248892ea20SAggelos Economopoulos #if 0
3257cc92483SSepherosa Ziehau 	/*
3267cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3277cc92483SSepherosa Ziehau 	 * config space
3287cc92483SSepherosa Ziehau 	 */
3298892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3308892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3318892ea20SAggelos Economopoulos 		val |= 0x40;
3328892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3338892ea20SAggelos Economopoulos 		return;
3348892ea20SAggelos Economopoulos 	}
3358892ea20SAggelos Economopoulos #endif
3367cc92483SSepherosa Ziehau 	/*
3377cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3388892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3398892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3408892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3418892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3428892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3438892ea20SAggelos Economopoulos 	 */
3448892ea20SAggelos Economopoulos 
3458892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3468892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3478892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3488892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3498892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3508892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3518892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3528892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3538892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3548892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3558892ea20SAggelos Economopoulos 
3567cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3577cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3588892ea20SAggelos Economopoulos 
3598892ea20SAggelos Economopoulos 	/* map it into the kernel */
3608892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3618892ea20SAggelos Economopoulos 	if (va == NULL) {
3628892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3638892ea20SAggelos Economopoulos 		return;
3648892ea20SAggelos Economopoulos 	}
3658892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3668892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3678892ea20SAggelos Economopoulos 
3688892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3698892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3708892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3718892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3728892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3738892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3748892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3758892ea20SAggelos Economopoulos 		return;
3768892ea20SAggelos Economopoulos 	}
3778892ea20SAggelos Economopoulos 
3788892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
3798892ea20SAggelos Economopoulos 	val = *ptr32;
3808892ea20SAggelos Economopoulos 
3818892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
3828892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
3838892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3848892ea20SAggelos Economopoulos 		return;
3858892ea20SAggelos Economopoulos 	}
3868892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
3878892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3887cc92483SSepherosa Ziehau 	if (bootverbose) {
3897cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
3907cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
3918892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
3927cc92483SSepherosa Ziehau 	}
3938892ea20SAggelos Economopoulos }
39489d55360SSepherosa Ziehau 
39589d55360SSepherosa Ziehau #else	/* __i386__ || __x86_64__ */
39689d55360SSepherosa Ziehau 
3978892ea20SAggelos Economopoulos static void
3988892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
3998892ea20SAggelos Economopoulos {
4007cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4018892ea20SAggelos Economopoulos }
4028892ea20SAggelos Economopoulos 
40389d55360SSepherosa Ziehau #endif
4048892ea20SAggelos Economopoulos 
4058892ea20SAggelos Economopoulos static int
4068892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4078892ea20SAggelos Economopoulos {
4088892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4097cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4108892ea20SAggelos Economopoulos 	int status;
4118892ea20SAggelos Economopoulos 	uint32_t len;
4127cc92483SSepherosa Ziehau 	const char *test = " ";
4138892ea20SAggelos Economopoulos 
4147cc92483SSepherosa Ziehau 	/*
4157cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4168892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4178892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4188892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4198892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4208892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4218892ea20SAggelos Economopoulos 	 * transfers took to complete.
4228892ea20SAggelos Economopoulos 	 */
4238892ea20SAggelos Economopoulos 
4248892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4258892ea20SAggelos Economopoulos 
4268892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4278892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4288892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4298892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4308892ea20SAggelos Economopoulos 	if (status != 0) {
4318892ea20SAggelos Economopoulos 		test = "read";
4328892ea20SAggelos Economopoulos 		goto abort;
4338892ea20SAggelos Economopoulos 	}
4347cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4357cc92483SSepherosa Ziehau 
4368892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4378892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4388892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4398892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4408892ea20SAggelos Economopoulos 	if (status != 0) {
4418892ea20SAggelos Economopoulos 		test = "write";
4428892ea20SAggelos Economopoulos 		goto abort;
4438892ea20SAggelos Economopoulos 	}
4447cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4458892ea20SAggelos Economopoulos 
4468892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4478892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4488892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4498892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4508892ea20SAggelos Economopoulos 	if (status != 0) {
4518892ea20SAggelos Economopoulos 		test = "read/write";
4528892ea20SAggelos Economopoulos 		goto abort;
4538892ea20SAggelos Economopoulos 	}
4548892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4558892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4568892ea20SAggelos Economopoulos 
4578892ea20SAggelos Economopoulos abort:
4587cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4598892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4608892ea20SAggelos Economopoulos 		    test, status);
4617cc92483SSepherosa Ziehau 	}
4628892ea20SAggelos Economopoulos 	return status;
4638892ea20SAggelos Economopoulos }
4648892ea20SAggelos Economopoulos 
4658892ea20SAggelos Economopoulos /*
4668892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4678892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4688892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4698892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4708892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4718892ea20SAggelos Economopoulos  *
4728892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4738892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4748892ea20SAggelos Economopoulos  *
4758892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
4768892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
4778892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
4788892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
4798892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
4808892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
4818892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
4828892ea20SAggelos Economopoulos  */
4838892ea20SAggelos Economopoulos static int
4848892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
4858892ea20SAggelos Economopoulos {
4868892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
4878892ea20SAggelos Economopoulos 	int reg, status;
4888892ea20SAggelos Economopoulos 	uint16_t pectl;
4898892ea20SAggelos Economopoulos 
4908892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
4917cc92483SSepherosa Ziehau 
4928892ea20SAggelos Economopoulos 	/*
4938892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
4948892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
4958892ea20SAggelos Economopoulos 	 */
4968892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
4978892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
4988892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
4997cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5008892ea20SAggelos Economopoulos 			    pectl);
5018892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5028892ea20SAggelos Economopoulos 		}
5038892ea20SAggelos Economopoulos 	}
5048892ea20SAggelos Economopoulos 
5058892ea20SAggelos Economopoulos 	/*
5067cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5078892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5088892ea20SAggelos Economopoulos 	 */
5098892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5108892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5117cc92483SSepherosa Ziehau 	if (status != 0)
5128892ea20SAggelos Economopoulos 		return status;
5138892ea20SAggelos Economopoulos 
5148892ea20SAggelos Economopoulos 	/*
5158892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5168892ea20SAggelos Economopoulos 	 */
5178892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5188892ea20SAggelos Economopoulos 
5198892ea20SAggelos Economopoulos 	/*
5208892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
52189d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5228892ea20SAggelos Economopoulos 	 */
52389d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
52489d55360SSepherosa Ziehau 		return 0;
5258892ea20SAggelos Economopoulos 
5268892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5278892ea20SAggelos Economopoulos 	if (status == 0)
5288892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5298892ea20SAggelos Economopoulos 
5308892ea20SAggelos Economopoulos 	if (status != E2BIG)
5318892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5327cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5338892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5348892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5357cc92483SSepherosa Ziehau 	}
5368892ea20SAggelos Economopoulos 	return status;
5378892ea20SAggelos Economopoulos }
5388892ea20SAggelos Economopoulos 
5398892ea20SAggelos Economopoulos static int
5408892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5418892ea20SAggelos Economopoulos {
5428892ea20SAggelos Economopoulos 	int aligned = 0;
54389d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5448892ea20SAggelos Economopoulos 
54589d55360SSepherosa Ziehau 	if (sc->throttle)
54689d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5478892ea20SAggelos Economopoulos 
54889d55360SSepherosa Ziehau 	if (force_firmware != 0) {
54989d55360SSepherosa Ziehau 		if (force_firmware == 1)
5508892ea20SAggelos Economopoulos 			aligned = 1;
5518892ea20SAggelos Economopoulos 		else
5528892ea20SAggelos Economopoulos 			aligned = 0;
5537cc92483SSepherosa Ziehau 		if (bootverbose) {
5548892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5558892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5568892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5577cc92483SSepherosa Ziehau 		}
5588892ea20SAggelos Economopoulos 		goto abort;
5598892ea20SAggelos Economopoulos 	}
5608892ea20SAggelos Economopoulos 
5617cc92483SSepherosa Ziehau 	/*
5627cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5637cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5647cc92483SSepherosa Ziehau 	 */
5658892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5667cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5677cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5688892ea20SAggelos Economopoulos 		aligned = 1;
5698892ea20SAggelos Economopoulos 		goto abort;
5708892ea20SAggelos Economopoulos 	}
5718892ea20SAggelos Economopoulos 
5727cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5738892ea20SAggelos Economopoulos 		return 0;
5748892ea20SAggelos Economopoulos 
5758892ea20SAggelos Economopoulos abort:
5768892ea20SAggelos Economopoulos 	if (aligned) {
5778892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
5788892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
5798892ea20SAggelos Economopoulos 	} else {
5808892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
5818892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
5828892ea20SAggelos Economopoulos 	}
5837cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
5848892ea20SAggelos Economopoulos }
5858892ea20SAggelos Economopoulos 
5868892ea20SAggelos Economopoulos static int
5878892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
5888892ea20SAggelos Economopoulos {
5898892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
5908a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware type: 0x%x\n",
5918892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
5928892ea20SAggelos Economopoulos 		return EIO;
5938892ea20SAggelos Economopoulos 	}
5948892ea20SAggelos Economopoulos 
5957cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
5967cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
5977cc92483SSepherosa Ziehau 	if (bootverbose)
5988a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "firmware id: %s\n", hdr->version);
5998892ea20SAggelos Economopoulos 
600b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6018892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6028892ea20SAggelos Economopoulos 
6037cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6047cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6058a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Found firmware version %s\n",
6068892ea20SAggelos Economopoulos 		    sc->fw_version);
6078a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Driver needs %d.%d\n",
6088892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6098892ea20SAggelos Economopoulos 		return EINVAL;
6108892ea20SAggelos Economopoulos 	}
6118892ea20SAggelos Economopoulos 	return 0;
6128892ea20SAggelos Economopoulos }
6138892ea20SAggelos Economopoulos 
6148892ea20SAggelos Economopoulos static void *
6158892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6168892ea20SAggelos Economopoulos {
6177cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6188892ea20SAggelos Economopoulos }
6198892ea20SAggelos Economopoulos 
6208892ea20SAggelos Economopoulos static void
6218892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6228892ea20SAggelos Economopoulos {
623d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6248892ea20SAggelos Economopoulos }
625d83c779aSSascha Wildner 
6268892ea20SAggelos Economopoulos static int
6278892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6288892ea20SAggelos Economopoulos {
629d83c779aSSascha Wildner 	z_stream zs;
630d83c779aSSascha Wildner 	char *inflate_buffer;
631d83c779aSSascha Wildner 	const struct firmware *fw;
6328892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6338892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6348892ea20SAggelos Economopoulos 	int status;
6358892ea20SAggelos Economopoulos 	unsigned int i;
63689d55360SSepherosa Ziehau 	char dummy;
6378892ea20SAggelos Economopoulos 	size_t fw_len;
6388892ea20SAggelos Economopoulos 
639d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6408892ea20SAggelos Economopoulos 	if (fw == NULL) {
6418a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Could not find firmware image %s\n",
6428892ea20SAggelos Economopoulos 		    sc->fw_name);
6438892ea20SAggelos Economopoulos 		return ENOENT;
6448892ea20SAggelos Economopoulos 	}
645d83c779aSSascha Wildner 
6467cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6478892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6488892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6498892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6508892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6518892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6528892ea20SAggelos Economopoulos 		status = EIO;
6538892ea20SAggelos Economopoulos 		goto abort_with_fw;
6548892ea20SAggelos Economopoulos 	}
6558892ea20SAggelos Economopoulos 
6567cc92483SSepherosa Ziehau 	/*
6577cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6587cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6597cc92483SSepherosa Ziehau 	 */
6608892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6617cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6628892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6638892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6648892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6658892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6668892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6678892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6688a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "zlib %d\n", status);
6698892ea20SAggelos Economopoulos 		status = EIO;
6708892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6718892ea20SAggelos Economopoulos 	}
672d83c779aSSascha Wildner 
6737cc92483SSepherosa Ziehau 	/* Check id */
6747cc92483SSepherosa Ziehau 	hdr_offset =
6757cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
6768892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
6778a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware file");
6788892ea20SAggelos Economopoulos 		status = EIO;
679d83c779aSSascha Wildner 		goto abort_with_buffer;
6808892ea20SAggelos Economopoulos 	}
681d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
6828892ea20SAggelos Economopoulos 
6838892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
6848892ea20SAggelos Economopoulos 	if (status != 0)
685d83c779aSSascha Wildner 		goto abort_with_buffer;
6868892ea20SAggelos Economopoulos 
6878892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
6888892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
6897cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
6908892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
6918892ea20SAggelos Economopoulos 		wmb();
69289d55360SSepherosa Ziehau 		dummy = *sc->sram;
6938892ea20SAggelos Economopoulos 		wmb();
6948892ea20SAggelos Economopoulos 	}
6958892ea20SAggelos Economopoulos 
6968892ea20SAggelos Economopoulos 	*limit = fw_len;
6978892ea20SAggelos Economopoulos 	status = 0;
6988892ea20SAggelos Economopoulos abort_with_buffer:
699d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7008892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7018892ea20SAggelos Economopoulos abort_with_fw:
702d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7038892ea20SAggelos Economopoulos 	return status;
7048892ea20SAggelos Economopoulos }
7058892ea20SAggelos Economopoulos 
7068892ea20SAggelos Economopoulos /*
7078892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7088892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7098892ea20SAggelos Economopoulos  */
7108892ea20SAggelos Economopoulos static void
7118892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7128892ea20SAggelos Economopoulos {
7138892ea20SAggelos Economopoulos 	char buf_bytes[72];
7148892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7158892ea20SAggelos Economopoulos 	volatile char *submit;
7168892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7178892ea20SAggelos Economopoulos 	int i;
7188892ea20SAggelos Economopoulos 
7198892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7208892ea20SAggelos Economopoulos 
7217cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7228892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7238892ea20SAggelos Economopoulos 	*confirm = 0;
7248892ea20SAggelos Economopoulos 	wmb();
7258892ea20SAggelos Economopoulos 
7267cc92483SSepherosa Ziehau 	/*
7277cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7287cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7297cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7308892ea20SAggelos Economopoulos 	 */
7317cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7327cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7338892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7348892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7358892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7367cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7377cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7388892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7398892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7408892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7418892ea20SAggelos Economopoulos 
7428892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7438892ea20SAggelos Economopoulos 
7448892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7458892ea20SAggelos Economopoulos 	wmb();
7468892ea20SAggelos Economopoulos 	DELAY(1000);
7478892ea20SAggelos Economopoulos 	wmb();
7488892ea20SAggelos Economopoulos 	i = 0;
7498892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7508892ea20SAggelos Economopoulos 		DELAY(1000);
7518892ea20SAggelos Economopoulos 		i++;
7528892ea20SAggelos Economopoulos 	}
7538892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7546ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7557cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7568892ea20SAggelos Economopoulos 	}
7578892ea20SAggelos Economopoulos }
7588892ea20SAggelos Economopoulos 
7598892ea20SAggelos Economopoulos static int
7608892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7618892ea20SAggelos Economopoulos {
7628892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7638892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7648892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7658892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7668892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7678892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7688892ea20SAggelos Economopoulos 
7696ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7708892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7718892ea20SAggelos Economopoulos 
7728892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7738892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7748892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
7758892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
7767cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7777cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7788892ea20SAggelos Economopoulos 
7798892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
7808892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
7812e8181d0SAggelos Economopoulos 
7828892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
7838892ea20SAggelos Economopoulos 	wmb();
7848892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
7858892ea20SAggelos Economopoulos 
7866ee6cba3SSepherosa Ziehau 	/*
7876ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
7886ee6cba3SSepherosa Ziehau 	 */
7898892ea20SAggelos Economopoulos 	err = EAGAIN;
7908892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
7918892ea20SAggelos Economopoulos 		wmb();
7928892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
7938892ea20SAggelos Economopoulos 		case 0:
7948892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
7958892ea20SAggelos Economopoulos 			err = 0;
7968892ea20SAggelos Economopoulos 			break;
7978892ea20SAggelos Economopoulos 		case 0xffffffff:
7988892ea20SAggelos Economopoulos 			DELAY(1000);
7998892ea20SAggelos Economopoulos 			break;
8008892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8018892ea20SAggelos Economopoulos 			err = ENOSYS;
8028892ea20SAggelos Economopoulos 			break;
8038892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8048892ea20SAggelos Economopoulos 			err = E2BIG;
8058892ea20SAggelos Economopoulos 			break;
8068892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8078892ea20SAggelos Economopoulos 			err = EBUSY;
8088892ea20SAggelos Economopoulos 			break;
80989d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
81089d55360SSepherosa Ziehau 			err = ENXIO;
81189d55360SSepherosa Ziehau 			break;
8128892ea20SAggelos Economopoulos 		default:
8136ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8148892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8158892ea20SAggelos Economopoulos 			err = ENXIO;
8168892ea20SAggelos Economopoulos 			break;
8178892ea20SAggelos Economopoulos 		}
8188892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8198892ea20SAggelos Economopoulos 			break;
8208892ea20SAggelos Economopoulos 	}
8216ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8226ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8238892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8246ee6cba3SSepherosa Ziehau 	}
8258892ea20SAggelos Economopoulos 	return err;
8268892ea20SAggelos Economopoulos }
8278892ea20SAggelos Economopoulos 
8288892ea20SAggelos Economopoulos static int
8298892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8308892ea20SAggelos Economopoulos {
8318892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8328892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8338892ea20SAggelos Economopoulos 	size_t hdr_offset;
8348892ea20SAggelos Economopoulos 	int status;
8358892ea20SAggelos Economopoulos 
8367cc92483SSepherosa Ziehau 	/*
8377cc92483SSepherosa Ziehau 	 * Find running firmware header
8387cc92483SSepherosa Ziehau 	 */
8397cc92483SSepherosa Ziehau 	hdr_offset =
8407cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8418892ea20SAggelos Economopoulos 
8428892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8438a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Running firmware has bad header offset "
8448a20b038SSepherosa Ziehau 		    "(%zu)\n", hdr_offset);
8458892ea20SAggelos Economopoulos 		return EIO;
8468892ea20SAggelos Economopoulos 	}
8478892ea20SAggelos Economopoulos 
8487cc92483SSepherosa Ziehau 	/*
8497cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8507cc92483SSepherosa Ziehau 	 * validate firmware
8517cc92483SSepherosa Ziehau 	 */
8527cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8538892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8547cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8558892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
856d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8578892ea20SAggelos Economopoulos 
8588892ea20SAggelos Economopoulos 	/*
8597cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8608892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8618892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8628892ea20SAggelos Economopoulos 	 */
8638892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8648892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8658892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8668a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Adopting fw %d.%d.%d: "
8678892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8687cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8698892ea20SAggelos Economopoulos 	}
8708892ea20SAggelos Economopoulos 
8718892ea20SAggelos Economopoulos 	return status;
8728892ea20SAggelos Economopoulos }
8738892ea20SAggelos Economopoulos 
8748892ea20SAggelos Economopoulos static int
8758892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
8768892ea20SAggelos Economopoulos {
8778892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
8788892ea20SAggelos Economopoulos 	volatile char *submit;
8798892ea20SAggelos Economopoulos 	char buf_bytes[72];
8808892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
8818892ea20SAggelos Economopoulos 	int status, i;
8828892ea20SAggelos Economopoulos 
8838892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
8848892ea20SAggelos Economopoulos 
8858892ea20SAggelos Economopoulos 	size = sc->sram_size;
8868892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
8878892ea20SAggelos Economopoulos 	if (status) {
8888892ea20SAggelos Economopoulos 		if (!adopt)
8898892ea20SAggelos Economopoulos 			return status;
8907cc92483SSepherosa Ziehau 
8917cc92483SSepherosa Ziehau 		/*
8927cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
8937cc92483SSepherosa Ziehau 		 * it is new enough
8947cc92483SSepherosa Ziehau 		 */
8958892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
8968892ea20SAggelos Economopoulos 		if (status) {
8978a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
8988892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
8998892ea20SAggelos Economopoulos 			return status;
9008892ea20SAggelos Economopoulos 		}
9018a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Successfully adopted running firmware\n");
9027cc92483SSepherosa Ziehau 
9038892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9048a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9057cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9067cc92483SSepherosa Ziehau 			     "For optimal\n");
9078a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "performance consider loading "
9087cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9098892ea20SAggelos Economopoulos 		}
9108892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9118892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9128892ea20SAggelos Economopoulos 		return 0;
9138892ea20SAggelos Economopoulos 	}
9147cc92483SSepherosa Ziehau 
9157cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9168892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9178892ea20SAggelos Economopoulos 	*confirm = 0;
9188892ea20SAggelos Economopoulos 	wmb();
9197cc92483SSepherosa Ziehau 
9207cc92483SSepherosa Ziehau 	/*
9217cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9227cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9237cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9248892ea20SAggelos Economopoulos 	 */
9258892ea20SAggelos Economopoulos 
9267cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9277cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9288892ea20SAggelos Economopoulos 
9298892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9308892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9318892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9328892ea20SAggelos Economopoulos 
9337cc92483SSepherosa Ziehau 	/*
9347cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9357cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9367cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9378892ea20SAggelos Economopoulos 	 */
9388892ea20SAggelos Economopoulos 					/* where the code starts*/
9398892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9408892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9418892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9428892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9438892ea20SAggelos Economopoulos 
9448892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9458892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9468892ea20SAggelos Economopoulos 	wmb();
9478892ea20SAggelos Economopoulos 	DELAY(1000);
9488892ea20SAggelos Economopoulos 	wmb();
9498892ea20SAggelos Economopoulos 	i = 0;
9508892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9518892ea20SAggelos Economopoulos 		DELAY(1000*10);
9528892ea20SAggelos Economopoulos 		i++;
9538892ea20SAggelos Economopoulos 	}
9548892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9558a20b038SSepherosa Ziehau 		if_printf(sc->ifp,"handoff failed (%p = 0x%x)",
9568892ea20SAggelos Economopoulos 		    confirm, *confirm);
9578892ea20SAggelos Economopoulos 		return ENXIO;
9588892ea20SAggelos Economopoulos 	}
9598892ea20SAggelos Economopoulos 	return 0;
9608892ea20SAggelos Economopoulos }
9618892ea20SAggelos Economopoulos 
9628892ea20SAggelos Economopoulos static int
9638892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9648892ea20SAggelos Economopoulos {
9658892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9668892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9678892ea20SAggelos Economopoulos 
9687cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9697cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9707cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9717cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9728892ea20SAggelos Economopoulos }
9738892ea20SAggelos Economopoulos 
9748892ea20SAggelos Economopoulos static int
9758892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
9768892ea20SAggelos Economopoulos {
9778892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9788892ea20SAggelos Economopoulos 	int status;
9798892ea20SAggelos Economopoulos 
9808892ea20SAggelos Economopoulos 	if (pause)
9817cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
9828892ea20SAggelos Economopoulos 	else
9837cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
9848892ea20SAggelos Economopoulos 	if (status) {
9855a637e78SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set flow control mode\n");
9868892ea20SAggelos Economopoulos 		return ENXIO;
9878892ea20SAggelos Economopoulos 	}
9888892ea20SAggelos Economopoulos 	sc->pause = pause;
9898892ea20SAggelos Economopoulos 	return 0;
9908892ea20SAggelos Economopoulos }
9918892ea20SAggelos Economopoulos 
9928892ea20SAggelos Economopoulos static void
9938892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
9948892ea20SAggelos Economopoulos {
9958892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9968892ea20SAggelos Economopoulos 	int status;
9978892ea20SAggelos Economopoulos 
9988892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
9998892ea20SAggelos Economopoulos 		promisc = 1;
10008892ea20SAggelos Economopoulos 
10018892ea20SAggelos Economopoulos 	if (promisc)
10027cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10038892ea20SAggelos Economopoulos 	else
10047cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10057cc92483SSepherosa Ziehau 	if (status)
1006af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set promisc mode\n");
10078892ea20SAggelos Economopoulos }
10088892ea20SAggelos Economopoulos 
10098892ea20SAggelos Economopoulos static void
10108892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10118892ea20SAggelos Economopoulos {
10128892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10138892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10148892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10158892ea20SAggelos Economopoulos 	int err;
10168892ea20SAggelos Economopoulos 
10178892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10188892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10198892ea20SAggelos Economopoulos 		return;
10208892ea20SAggelos Economopoulos 
10218892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
10228892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10238892ea20SAggelos Economopoulos 	if (err != 0) {
1024af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_ENABLE_ALLMULTI, "
10258892ea20SAggelos Economopoulos 		    "error status: %d\n", err);
10268892ea20SAggelos Economopoulos 		return;
10278892ea20SAggelos Economopoulos 	}
10288892ea20SAggelos Economopoulos 
10298892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10308892ea20SAggelos Economopoulos 		return;
10318892ea20SAggelos Economopoulos 
10327cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10337cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10348892ea20SAggelos Economopoulos 		return;
10358892ea20SAggelos Economopoulos 	}
10368892ea20SAggelos Economopoulos 
10377cc92483SSepherosa Ziehau 	/* Flush all the filters */
10387cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10397cc92483SSepherosa Ziehau 	if (err != 0) {
1040af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10417cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10427cc92483SSepherosa Ziehau 		return;
10437cc92483SSepherosa Ziehau 	}
10448892ea20SAggelos Economopoulos 
10457cc92483SSepherosa Ziehau 	/*
10467cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10477cc92483SSepherosa Ziehau 	 */
1048441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10498892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10508892ea20SAggelos Economopoulos 			continue;
10517cc92483SSepherosa Ziehau 
10528892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10538892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10548892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10558892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10568892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10578892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10588892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10598892ea20SAggelos Economopoulos 		if (err != 0) {
1060af85d4d5SSepherosa Ziehau 			if_printf(ifp, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
10617cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10627cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10638892ea20SAggelos Economopoulos 			return;
10648892ea20SAggelos Economopoulos 		}
10658892ea20SAggelos Economopoulos 	}
10667cc92483SSepherosa Ziehau 
10678892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10688892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10698892ea20SAggelos Economopoulos 	if (err != 0) {
1070af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_DISABLE_ALLMULTI, "
10717cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10728892ea20SAggelos Economopoulos 	}
10738892ea20SAggelos Economopoulos }
10748892ea20SAggelos Economopoulos 
107589d55360SSepherosa Ziehau #if 0
10768892ea20SAggelos Economopoulos static int
10778892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
10788892ea20SAggelos Economopoulos {
10798892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10808892ea20SAggelos Economopoulos 	int status;
10818892ea20SAggelos Economopoulos 
10828892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
10838892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10848892ea20SAggelos Economopoulos 
10858892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
10868892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
10878892ea20SAggelos Economopoulos 	cmd.data0 = 0;
10888892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
10898892ea20SAggelos Economopoulos 			       &cmd);
10908892ea20SAggelos Economopoulos 	if (status == 0)
10918892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
10928892ea20SAggelos Economopoulos 
10938892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
10948892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
10958892ea20SAggelos Economopoulos }
109689d55360SSepherosa Ziehau #endif
10978892ea20SAggelos Economopoulos 
10988892ea20SAggelos Economopoulos static int
10998892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11008892ea20SAggelos Economopoulos {
11018892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11028892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11038892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11048892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11058892ea20SAggelos Economopoulos 	int slice, status;
11068892ea20SAggelos Economopoulos 
11077cc92483SSepherosa Ziehau 	/*
11087cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11097cc92483SSepherosa Ziehau 	 * is alive
11107cc92483SSepherosa Ziehau 	 */
11118892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11128892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11138892ea20SAggelos Economopoulos 	if (status != 0) {
11146ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11158892ea20SAggelos Economopoulos 		return ENXIO;
11168892ea20SAggelos Economopoulos 	}
11178892ea20SAggelos Economopoulos 
11188892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11198892ea20SAggelos Economopoulos 
11207cc92483SSepherosa Ziehau 	/* Set the intrq size */
11218892ea20SAggelos Economopoulos 	cmd.data0 = sc->rx_ring_size;
11228892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11238892ea20SAggelos Economopoulos 
11248892ea20SAggelos Economopoulos 	/*
11258892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11268892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11278892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11288892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11298892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11308892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11318892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11328892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11338892ea20SAggelos Economopoulos 	 */
11348892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11357cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11367cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11378892ea20SAggelos Economopoulos 		if (status != 0) {
11386ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11398892ea20SAggelos Economopoulos 			return status;
11408892ea20SAggelos Economopoulos 		}
11417cc92483SSepherosa Ziehau 
11428892ea20SAggelos Economopoulos 		/*
11438892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11448892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11458892ea20SAggelos Economopoulos 		 */
11468892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11478892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
11488892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
11498892ea20SAggelos Economopoulos 		cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11508892ea20SAggelos Economopoulos #endif
11517cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11528892ea20SAggelos Economopoulos 		if (status != 0) {
11536ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11548892ea20SAggelos Economopoulos 			return status;
11558892ea20SAggelos Economopoulos 		}
11568892ea20SAggelos Economopoulos 	}
11578892ea20SAggelos Economopoulos 
11588892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11598892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11608892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
11618892ea20SAggelos Economopoulos 			rx_done = &sc->ss[slice].rx_done;
11628892ea20SAggelos Economopoulos 			memset(rx_done->entry, 0, sc->rx_ring_size);
11637cc92483SSepherosa Ziehau 			cmd.data0 =
11647cc92483SSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(rx_done->dma.dmem_busaddr);
11657cc92483SSepherosa Ziehau 			cmd.data1 =
11667cc92483SSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(rx_done->dma.dmem_busaddr);
11678892ea20SAggelos Economopoulos 			cmd.data2 = slice;
11687cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
11698892ea20SAggelos Economopoulos 			    &cmd);
11708892ea20SAggelos Economopoulos 		}
11718892ea20SAggelos Economopoulos 	}
11728892ea20SAggelos Economopoulos 
11737cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
11747cc92483SSepherosa Ziehau 	    &cmd);
11758892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
11768892ea20SAggelos Economopoulos 
11778892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
11788892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
11798892ea20SAggelos Economopoulos 
11807cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
11818892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
11827cc92483SSepherosa Ziehau 
11838892ea20SAggelos Economopoulos 	if (status != 0) {
11846ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
11858892ea20SAggelos Economopoulos 		return status;
11868892ea20SAggelos Economopoulos 	}
11878892ea20SAggelos Economopoulos 
11888892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
11898892ea20SAggelos Economopoulos 
11907cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
11917cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
11928892ea20SAggelos Economopoulos 
11938892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
11948892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
11958892ea20SAggelos Economopoulos 
11968892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
11977cc92483SSepherosa Ziehau 
11987cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
11998892ea20SAggelos Economopoulos 		ss->rx_done.idx = 0;
12008892ea20SAggelos Economopoulos 		ss->rx_done.cnt = 0;
12018892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12028892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12038892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12048892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12058892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12068892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12078892ea20SAggelos Economopoulos 		ss->rx_big.cnt = 0;
12088892ea20SAggelos Economopoulos 		ss->rx_small.cnt = 0;
12097cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12107cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12118892ea20SAggelos Economopoulos 	}
12128892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12137cc92483SSepherosa Ziehau 
12148892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12158892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12168892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12178892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12187cc92483SSepherosa Ziehau 
121989d55360SSepherosa Ziehau 	if (sc->throttle) {
122089d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12217cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12226ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
122389d55360SSepherosa Ziehau 	}
12248892ea20SAggelos Economopoulos 	return status;
12258892ea20SAggelos Economopoulos }
12268892ea20SAggelos Economopoulos 
12278892ea20SAggelos Economopoulos static int
122889d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
122989d55360SSepherosa Ziehau {
123089d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
123189d55360SSepherosa Ziehau 	mxge_softc_t *sc;
123289d55360SSepherosa Ziehau 	int err;
123389d55360SSepherosa Ziehau 	unsigned int throttle;
123489d55360SSepherosa Ziehau 
123589d55360SSepherosa Ziehau 	sc = arg1;
123689d55360SSepherosa Ziehau 	throttle = sc->throttle;
123789d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12385a637e78SSepherosa Ziehau 	if (err != 0)
123989d55360SSepherosa Ziehau 		return err;
124089d55360SSepherosa Ziehau 
124189d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
124289d55360SSepherosa Ziehau 		return 0;
124389d55360SSepherosa Ziehau 
124489d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
124589d55360SSepherosa Ziehau 		return EINVAL;
124689d55360SSepherosa Ziehau 
124789d55360SSepherosa Ziehau 	lwkt_serialize_enter(sc->ifp->if_serializer);
124889d55360SSepherosa Ziehau 
124989d55360SSepherosa Ziehau 	cmd.data0 = throttle;
125089d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
125189d55360SSepherosa Ziehau 	if (err == 0)
125289d55360SSepherosa Ziehau 		sc->throttle = throttle;
125389d55360SSepherosa Ziehau 
125489d55360SSepherosa Ziehau 	lwkt_serialize_exit(sc->ifp->if_serializer);
125589d55360SSepherosa Ziehau 	return err;
125689d55360SSepherosa Ziehau }
125789d55360SSepherosa Ziehau 
125889d55360SSepherosa Ziehau static int
12598892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
12608892ea20SAggelos Economopoulos {
12618892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12628892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
12638892ea20SAggelos Economopoulos 	int err;
12648892ea20SAggelos Economopoulos 
12658892ea20SAggelos Economopoulos 	sc = arg1;
12668892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
12678892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
12685a637e78SSepherosa Ziehau 	if (err != 0)
12698892ea20SAggelos Economopoulos 		return err;
12705a637e78SSepherosa Ziehau 
12718892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
12728892ea20SAggelos Economopoulos 		return 0;
12738892ea20SAggelos Economopoulos 
12748892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
12758892ea20SAggelos Economopoulos 		return EINVAL;
12768892ea20SAggelos Economopoulos 
12772e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
127889d55360SSepherosa Ziehau 
12798892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
12808892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
12818892ea20SAggelos Economopoulos 
12822e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
12838892ea20SAggelos Economopoulos 	return err;
12848892ea20SAggelos Economopoulos }
12858892ea20SAggelos Economopoulos 
12868892ea20SAggelos Economopoulos static int
12878892ea20SAggelos Economopoulos mxge_change_flow_control(SYSCTL_HANDLER_ARGS)
12888892ea20SAggelos Economopoulos {
12898892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
12908892ea20SAggelos Economopoulos 	unsigned int enabled;
12918892ea20SAggelos Economopoulos 	int err;
12928892ea20SAggelos Economopoulos 
12938892ea20SAggelos Economopoulos 	sc = arg1;
12948892ea20SAggelos Economopoulos 	enabled = sc->pause;
12958892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &enabled, arg2, req);
12965a637e78SSepherosa Ziehau 	if (err != 0)
12978892ea20SAggelos Economopoulos 		return err;
12985a637e78SSepherosa Ziehau 
12998892ea20SAggelos Economopoulos 	if (enabled == sc->pause)
13008892ea20SAggelos Economopoulos 		return 0;
13018892ea20SAggelos Economopoulos 
13022e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
13038892ea20SAggelos Economopoulos 	err = mxge_change_pause(sc, enabled);
13042e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
13058892ea20SAggelos Economopoulos 
13068892ea20SAggelos Economopoulos 	return err;
13078892ea20SAggelos Economopoulos }
13088892ea20SAggelos Economopoulos 
13098892ea20SAggelos Economopoulos static int
13108892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13118892ea20SAggelos Economopoulos {
13128892ea20SAggelos Economopoulos 	int err;
13138892ea20SAggelos Economopoulos 
13148892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13158892ea20SAggelos Economopoulos 		return EFAULT;
13168892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13178892ea20SAggelos Economopoulos 	arg1 = NULL;
13188892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13198892ea20SAggelos Economopoulos 
13208892ea20SAggelos Economopoulos 	return err;
13218892ea20SAggelos Economopoulos }
13228892ea20SAggelos Economopoulos 
13238892ea20SAggelos Economopoulos static void
13248892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13258892ea20SAggelos Economopoulos {
1326798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13278892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13288892ea20SAggelos Economopoulos 		int slice;
13298892ea20SAggelos Economopoulos 
13308892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13318892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1332798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13338892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13348892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13358892ea20SAggelos Economopoulos 			}
1336798c3369SSepherosa Ziehau 		}
1337798c3369SSepherosa Ziehau 	}
1338798c3369SSepherosa Ziehau 
1339798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13408892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13418892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1342798c3369SSepherosa Ziehau 	}
1343798c3369SSepherosa Ziehau 
1344798c3369SSepherosa Ziehau 	if (sc->sysctl_tree != NULL) {
1345bbac37fbSAggelos Economopoulos 		sysctl_ctx_free(&sc->sysctl_ctx);
1346bbac37fbSAggelos Economopoulos 		sc->sysctl_tree = NULL;
13478892ea20SAggelos Economopoulos 	}
1348798c3369SSepherosa Ziehau }
13498892ea20SAggelos Economopoulos 
13508892ea20SAggelos Economopoulos static void
13518892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13528892ea20SAggelos Economopoulos {
13538892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13548892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13558892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13568892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13578892ea20SAggelos Economopoulos 	int slice;
13588892ea20SAggelos Economopoulos 	char slice_num[8];
13598892ea20SAggelos Economopoulos 
1360b6737651SAggelos Economopoulos 	ctx = &sc->sysctl_ctx;
1361b6737651SAggelos Economopoulos 	sysctl_ctx_init(ctx);
1362b6737651SAggelos Economopoulos 	sc->sysctl_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
13637cc92483SSepherosa Ziehau 	    OID_AUTO, device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "");
1364b6737651SAggelos Economopoulos 	if (sc->sysctl_tree == NULL) {
1365b6737651SAggelos Economopoulos 		device_printf(sc->dev, "can't add sysctl node\n");
1366b6737651SAggelos Economopoulos 		return;
1367b6737651SAggelos Economopoulos 	}
1368b6737651SAggelos Economopoulos 
1369b6737651SAggelos Economopoulos 	children = SYSCTL_CHILDREN(sc->sysctl_tree);
13708892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13718892ea20SAggelos Economopoulos 
13727cc92483SSepherosa Ziehau 	/*
13737cc92483SSepherosa Ziehau 	 * Random information
13747cc92483SSepherosa Ziehau 	 */
13757cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13767cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
13778892ea20SAggelos Economopoulos 
13787cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
13797cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
13808892ea20SAggelos Economopoulos 
13817cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
13827cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
13838892ea20SAggelos Economopoulos 
13847cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
13857cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
138689d55360SSepherosa Ziehau 
13877cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
13887cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
13898892ea20SAggelos Economopoulos 
13907cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
13917cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
13928892ea20SAggelos Economopoulos 
13937cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
13947cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
13958892ea20SAggelos Economopoulos 
13967cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
13977cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
13988892ea20SAggelos Economopoulos 
13997cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14007cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14017cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14027cc92483SSepherosa Ziehau 
14037cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14047cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14057cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14067cc92483SSepherosa Ziehau 
14077cc92483SSepherosa Ziehau 	/*
14087cc92483SSepherosa Ziehau 	 * Performance related tunables
14097cc92483SSepherosa Ziehau 	 */
14107cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14117cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14127cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14137cc92483SSepherosa Ziehau 
14147cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14157cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14167cc92483SSepherosa Ziehau 	    "Transmit throttling");
14177cc92483SSepherosa Ziehau 
14187cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "flow_control_enabled",
14197cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_flow_control, "I",
14207cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14217cc92483SSepherosa Ziehau 
14227cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14237cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14247cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14257cc92483SSepherosa Ziehau 
14267cc92483SSepherosa Ziehau 	/*
14277cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14287cc92483SSepherosa Ziehau 	 * Need to swap it
14297cc92483SSepherosa Ziehau 	 */
14307cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14317cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14327cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14337cc92483SSepherosa Ziehau 
14347cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14357cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14367cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14377cc92483SSepherosa Ziehau 
14387cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14397cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14407cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14417cc92483SSepherosa Ziehau 
14427cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14437cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14447cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14457cc92483SSepherosa Ziehau 
14467cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14477cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14487cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14497cc92483SSepherosa Ziehau 
14507cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14517cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14527cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14537cc92483SSepherosa Ziehau 
14547cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14557cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14567cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14577cc92483SSepherosa Ziehau 
14587cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14597cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14607cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14617cc92483SSepherosa Ziehau 
14627cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14637cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14647cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14657cc92483SSepherosa Ziehau 
14667cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14677cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14687cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14697cc92483SSepherosa Ziehau 
14707cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14717cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14727cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14737cc92483SSepherosa Ziehau 
14747cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14757cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14767cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
14777cc92483SSepherosa Ziehau 
14787cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
14797cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
14807cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
14818892ea20SAggelos Economopoulos 
14828892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
14838892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
14847cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
14857cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1486798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1487798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1488798c3369SSepherosa Ziehau 		return;
1489798c3369SSepherosa Ziehau 	}
14908892ea20SAggelos Economopoulos 
14918892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
14928892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
14938892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
14948892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
14958892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1496b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
14977cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
14987cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1499798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1500798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1501798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1502798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1503798c3369SSepherosa Ziehau 		}
15048892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15057cc92483SSepherosa Ziehau 
15067cc92483SSepherosa Ziehau 		/*
15077cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15087cc92483SSepherosa Ziehau 		 */
15097cc92483SSepherosa Ziehau 
15107cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15117cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_small.cnt, 0, "rx_small_cnt");
15127cc92483SSepherosa Ziehau 
15137cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15147cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_big.cnt, 0, "rx_small_cnt");
15158892ea20SAggelos Economopoulos 
15168892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
15178892ea20SAggelos Economopoulos 		/* only transmit from slice 0 for now */
15188892ea20SAggelos Economopoulos 		if (slice > 0)
15198892ea20SAggelos Economopoulos 			continue;
15208892ea20SAggelos Economopoulos #endif
15218892ea20SAggelos Economopoulos 
15227cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15237cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15247cc92483SSepherosa Ziehau 
15257cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15267cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15277cc92483SSepherosa Ziehau 
15287cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15297cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15307cc92483SSepherosa Ziehau 
15317cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15327cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15337cc92483SSepherosa Ziehau 
15347cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15357cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15367cc92483SSepherosa Ziehau 
15377cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15387cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15398892ea20SAggelos Economopoulos 	}
15408892ea20SAggelos Economopoulos }
15418892ea20SAggelos Economopoulos 
154289d55360SSepherosa Ziehau /*
154389d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
154489d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
154589d55360SSepherosa Ziehau  */
1546ddbf91b7SSepherosa Ziehau static __inline void
15478892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15488892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15498892ea20SAggelos Economopoulos {
15508892ea20SAggelos Economopoulos 	int idx, starting_slot;
15515ca32f31SSepherosa Ziehau 
15528892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15538892ea20SAggelos Economopoulos 	while (cnt > 1) {
15548892ea20SAggelos Economopoulos 		cnt--;
15558892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15565ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15578892ea20SAggelos Economopoulos 		wmb();
15588892ea20SAggelos Economopoulos 	}
15598892ea20SAggelos Economopoulos }
15608892ea20SAggelos Economopoulos 
15618892ea20SAggelos Economopoulos /*
156289d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15638892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15648892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15658892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15668892ea20SAggelos Economopoulos  */
1567ddbf91b7SSepherosa Ziehau static __inline void
156889d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15698892ea20SAggelos Economopoulos {
15708892ea20SAggelos Economopoulos 	int idx, i;
15718892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15728892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15738892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15748892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15758892ea20SAggelos Economopoulos 	uint8_t last_flags;
15768892ea20SAggelos Economopoulos 
15778892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15788892ea20SAggelos Economopoulos 
15798892ea20SAggelos Economopoulos 	last_flags = src->flags;
15808892ea20SAggelos Economopoulos 	src->flags = 0;
15818892ea20SAggelos Economopoulos 	wmb();
15828892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
15838892ea20SAggelos Economopoulos 	srcp = src;
15848892ea20SAggelos Economopoulos 
15858892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
15865ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
15878892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
15888892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
15898892ea20SAggelos Economopoulos 			srcp += 2;
15908892ea20SAggelos Economopoulos 			dstp += 2;
15918892ea20SAggelos Economopoulos 		}
15928892ea20SAggelos Economopoulos 	} else {
15935ca32f31SSepherosa Ziehau 		/*
15945ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
15955ca32f31SSepherosa Ziehau 		 * that it is submitted below
15965ca32f31SSepherosa Ziehau 		 */
15978892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
15988892ea20SAggelos Economopoulos 		i = 0;
15998892ea20SAggelos Economopoulos 	}
16008892ea20SAggelos Economopoulos 	if (i < cnt) {
16015ca32f31SSepherosa Ziehau 		/* Submit the first request */
16028892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16038892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16048892ea20SAggelos Economopoulos 	}
16058892ea20SAggelos Economopoulos 
16065ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16078892ea20SAggelos Economopoulos 	src->flags = last_flags;
16088892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16098892ea20SAggelos Economopoulos 	src_ints+=3;
16108892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16118892ea20SAggelos Economopoulos 	dst_ints+=3;
16128892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16138892ea20SAggelos Economopoulos 	tx->req += cnt;
16148892ea20SAggelos Economopoulos 	wmb();
16158892ea20SAggelos Economopoulos }
16168892ea20SAggelos Economopoulos 
161789d55360SSepherosa Ziehau static int
161889d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
161989d55360SSepherosa Ziehau {
162089d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
162189d55360SSepherosa Ziehau 	struct mbuf *m;
162289d55360SSepherosa Ziehau 
162389d55360SSepherosa Ziehau 	m = *mp;
162489d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
162589d55360SSepherosa Ziehau 
162689d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
162789d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
162889d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
162989d55360SSepherosa Ziehau 
163089d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
163189d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
163289d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
163389d55360SSepherosa Ziehau 
163489d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
163589d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
163689d55360SSepherosa Ziehau 		if (m == NULL) {
163789d55360SSepherosa Ziehau 			*mp = NULL;
163889d55360SSepherosa Ziehau 			return ENOBUFS;
163989d55360SSepherosa Ziehau 		}
164089d55360SSepherosa Ziehau 		*mp = m;
164189d55360SSepherosa Ziehau 	}
164289d55360SSepherosa Ziehau 	return 0;
164389d55360SSepherosa Ziehau }
16448892ea20SAggelos Economopoulos 
16458892ea20SAggelos Economopoulos static void
16465ca32f31SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mbuf *m, int busdma_seg_cnt)
16478892ea20SAggelos Economopoulos {
16488892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16498892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16508892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16518892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16528892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16538892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16548892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
16558892ea20SAggelos Economopoulos 
16568892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16578892ea20SAggelos Economopoulos 
16585ca32f31SSepherosa Ziehau 	/*
16595ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16605ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16618892ea20SAggelos Economopoulos 	 */
166289d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
166389d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16648892ea20SAggelos Economopoulos 
16655ca32f31SSepherosa Ziehau 	/*
16665ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16675ca32f31SSepherosa Ziehau 	 */
166889d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16698892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16708892ea20SAggelos Economopoulos 
16715ca32f31SSepherosa Ziehau 	/*
16725ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16735ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
16745ca32f31SSepherosa Ziehau 	 */
16758892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16768892ea20SAggelos Economopoulos 
16778892ea20SAggelos Economopoulos 	req = tx->req_list;
16788892ea20SAggelos Economopoulos 	seg = tx->seg_list;
16798892ea20SAggelos Economopoulos 	cnt = 0;
16808892ea20SAggelos Economopoulos 	rdma_count = 0;
16815ca32f31SSepherosa Ziehau 
16825ca32f31SSepherosa Ziehau 	/*
16835ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
16845ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
16855ca32f31SSepherosa Ziehau 	 * this is equal to "count".
16868892ea20SAggelos Economopoulos 	 *
16875ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
16885ca32f31SSepherosa Ziehau 	 * segment cut.
16898892ea20SAggelos Economopoulos 	 *
16905ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
16915ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
16925ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
16935ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
16945ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
16955ca32f31SSepherosa Ziehau 	 * RDMA.
16965ca32f31SSepherosa Ziehau 	 *
16975ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
16985ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
16995ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17008892ea20SAggelos Economopoulos 	 */
17018892ea20SAggelos Economopoulos 
17028892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17035ca32f31SSepherosa Ziehau 		/*
17045ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17055ca32f31SSepherosa Ziehau 		 */
17068892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17078892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17088892ea20SAggelos Economopoulos 		len = seg->ds_len;
17098892ea20SAggelos Economopoulos 
17108892ea20SAggelos Economopoulos 		while (len) {
17118892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17128892ea20SAggelos Economopoulos 			seglen = len;
17138892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17148892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17158892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17165ca32f31SSepherosa Ziehau 				/* Payload */
17178892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17188892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17198892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17208892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17215ca32f31SSepherosa Ziehau 				flags_next |=
17225ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17238892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17248892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17258892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17265ca32f31SSepherosa Ziehau 				/* Header ends */
17278892ea20SAggelos Economopoulos 				rdma_count = -1;
17288892ea20SAggelos Economopoulos 				cum_len_next = 0;
17298892ea20SAggelos Economopoulos 				seglen = -cum_len;
17308892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17318892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17328892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17338892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17348892ea20SAggelos Economopoulos 			}
17358892ea20SAggelos Economopoulos 
17368892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17378892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17388892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17398892ea20SAggelos Economopoulos 			req->pad = 0;
17408892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17418892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17428892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17435ca32f31SSepherosa Ziehau 			req->flags =
17445ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17458892ea20SAggelos Economopoulos 			low += seglen;
17468892ea20SAggelos Economopoulos 			len -= seglen;
17478892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17488892ea20SAggelos Economopoulos 			flags = flags_next;
17498892ea20SAggelos Economopoulos 			req++;
17508892ea20SAggelos Economopoulos 			cnt++;
17518892ea20SAggelos Economopoulos 			rdma_count++;
17528892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17538892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17548892ea20SAggelos Economopoulos 			else
17558892ea20SAggelos Economopoulos 				cksum_offset = 0;
17568892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17578892ea20SAggelos Economopoulos 				goto drop;
17588892ea20SAggelos Economopoulos 		}
17598892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17608892ea20SAggelos Economopoulos 		seg++;
17618892ea20SAggelos Economopoulos 	}
17628892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17638892ea20SAggelos Economopoulos 
17648892ea20SAggelos Economopoulos 	do {
17658892ea20SAggelos Economopoulos 		req--;
17668892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17678892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17688892ea20SAggelos Economopoulos 
17698892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
17708892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17718892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
17728892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
17738892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
17748892ea20SAggelos Economopoulos 		*tx->send_go = 1;
17758892ea20SAggelos Economopoulos 		tx->queue_active = 1;
17768892ea20SAggelos Economopoulos 		tx->activate++;
17778892ea20SAggelos Economopoulos 		wmb();
17788892ea20SAggelos Economopoulos 	}
17798892ea20SAggelos Economopoulos #endif
17808892ea20SAggelos Economopoulos 	return;
17818892ea20SAggelos Economopoulos 
17828892ea20SAggelos Economopoulos drop:
17838892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
17848892ea20SAggelos Economopoulos 	m_freem(m);
17855ca32f31SSepherosa Ziehau #if 0
17865ca32f31SSepherosa Ziehau 	/* TODO update oerror counter */
17875ca32f31SSepherosa Ziehau #endif
17888892ea20SAggelos Economopoulos }
17898892ea20SAggelos Economopoulos 
17908892ea20SAggelos Economopoulos static void
17918892ea20SAggelos Economopoulos mxge_encap(struct mxge_slice_state *ss, struct mbuf *m)
17928892ea20SAggelos Economopoulos {
17938892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
17948892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
17958892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
17968892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
179789d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
17988892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
17998892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
18008892ea20SAggelos Economopoulos 
18018892ea20SAggelos Economopoulos 	sc = ss->sc;
18028892ea20SAggelos Economopoulos 	tx = &ss->tx;
18038892ea20SAggelos Economopoulos 
180489d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
180589d55360SSepherosa Ziehau 		if (mxge_pullup_tso(&m))
18068892ea20SAggelos Economopoulos 			return;
18078892ea20SAggelos Economopoulos 	}
180889d55360SSepherosa Ziehau 
18095ca32f31SSepherosa Ziehau 	/*
18105ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18115ca32f31SSepherosa Ziehau 	 */
181289d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
181389d55360SSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, tx->info[idx].map, &m,
181489d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
181589d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
181689d55360SSepherosa Ziehau 		goto drop;
181789d55360SSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, tx->info[idx].map, BUS_DMASYNC_PREWRITE);
181889d55360SSepherosa Ziehau 	tx->info[idx].m = m;
181989d55360SSepherosa Ziehau 
18205ca32f31SSepherosa Ziehau 	/*
18215ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18225ca32f31SSepherosa Ziehau 	 */
182389d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
18245ca32f31SSepherosa Ziehau 		mxge_encap_tso(tx, m, cnt);
182589d55360SSepherosa Ziehau 		return;
182689d55360SSepherosa Ziehau 	}
18278892ea20SAggelos Economopoulos 
18288892ea20SAggelos Economopoulos 	req = tx->req_list;
18298892ea20SAggelos Economopoulos 	cksum_offset = 0;
18308892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18318892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18328892ea20SAggelos Economopoulos 
18335ca32f31SSepherosa Ziehau 	/*
18345ca32f31SSepherosa Ziehau 	 * Checksum offloading
18355ca32f31SSepherosa Ziehau 	 */
183689d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
183789d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18388892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18398892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18408892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18418892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18428892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18438892ea20SAggelos Economopoulos 	} else {
18448892ea20SAggelos Economopoulos 		odd_flag = 0;
18458892ea20SAggelos Economopoulos 	}
18468892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18478892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18488892ea20SAggelos Economopoulos 
18495ca32f31SSepherosa Ziehau 	/*
18505ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18515ca32f31SSepherosa Ziehau 	 */
18528892ea20SAggelos Economopoulos 	cum_len = 0;
18538892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18548892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18558892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18565ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18575ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18588892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18598892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18608892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18618892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18628892ea20SAggelos Economopoulos 		else
18638892ea20SAggelos Economopoulos 			cksum_offset = 0;
18648892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18658892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18668892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18678892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18688892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18698892ea20SAggelos Economopoulos 		seg++;
18708892ea20SAggelos Economopoulos 		req++;
18718892ea20SAggelos Economopoulos 		req->flags = 0;
18728892ea20SAggelos Economopoulos 	}
18738892ea20SAggelos Economopoulos 	req--;
18745ca32f31SSepherosa Ziehau 
18755ca32f31SSepherosa Ziehau 	/*
18765ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
18775ca32f31SSepherosa Ziehau 	 */
18788892ea20SAggelos Economopoulos 	if (cum_len < 60) {
18798892ea20SAggelos Economopoulos 		req++;
18808892ea20SAggelos Economopoulos 		req->addr_low =
18817cc92483SSepherosa Ziehau 		    htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
18828892ea20SAggelos Economopoulos 		req->addr_high =
18837cc92483SSepherosa Ziehau 		    htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr));
18848892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
18858892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
18868892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18878892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18888892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18898892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18908892ea20SAggelos Economopoulos 		cnt++;
18918892ea20SAggelos Economopoulos 	}
18928892ea20SAggelos Economopoulos 
18938892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
18948892ea20SAggelos Economopoulos #if 0
18958892ea20SAggelos Economopoulos 	/* print what the firmware will see */
18968892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18976c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
18988892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
18998892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19008892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19018892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19028892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19038892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19048892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19058892ea20SAggelos Economopoulos 	}
19066c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19078892ea20SAggelos Economopoulos #endif
19088892ea20SAggelos Economopoulos 	tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1;
19098892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19108892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
19118892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && tx->queue_active == 0) {
19128892ea20SAggelos Economopoulos 		/* tell the NIC to start polling this slice */
19138892ea20SAggelos Economopoulos 		*tx->send_go = 1;
19148892ea20SAggelos Economopoulos 		tx->queue_active = 1;
19158892ea20SAggelos Economopoulos 		tx->activate++;
19168892ea20SAggelos Economopoulos 		wmb();
19178892ea20SAggelos Economopoulos 	}
19188892ea20SAggelos Economopoulos #endif
19198892ea20SAggelos Economopoulos 	return;
19208892ea20SAggelos Economopoulos 
19218892ea20SAggelos Economopoulos drop:
19228892ea20SAggelos Economopoulos 	m_freem(m);
19238892ea20SAggelos Economopoulos 	ss->oerrors++;
19248892ea20SAggelos Economopoulos }
19258892ea20SAggelos Economopoulos 
19268892ea20SAggelos Economopoulos static void
1927f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19288892ea20SAggelos Economopoulos {
19298892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
19308892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
1931795c96bbSSepherosa Ziehau 	mxge_tx_ring_t *tx;
19328892ea20SAggelos Economopoulos 
1933f0a26983SSepherosa Ziehau 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
1934cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
1935795c96bbSSepherosa Ziehau 
1936795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1937795c96bbSSepherosa Ziehau 		return;
1938795c96bbSSepherosa Ziehau 
1939795c96bbSSepherosa Ziehau 	/* XXX Only use the first slice for now */
19408892ea20SAggelos Economopoulos 	ss = &sc->ss[0];
1941795c96bbSSepherosa Ziehau 	tx = &ss->tx;
1942795c96bbSSepherosa Ziehau 
1943795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1944795c96bbSSepherosa Ziehau 		struct mbuf *m;
1945795c96bbSSepherosa Ziehau 
1946795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1947795c96bbSSepherosa Ziehau 		if (m == NULL)
1948795c96bbSSepherosa Ziehau 			return;
1949795c96bbSSepherosa Ziehau 
1950795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
1951795c96bbSSepherosa Ziehau 		mxge_encap(ss, m);
1952795c96bbSSepherosa Ziehau 	}
1953795c96bbSSepherosa Ziehau 
1954795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1955795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
19568892ea20SAggelos Economopoulos }
19578892ea20SAggelos Economopoulos 
19588892ea20SAggelos Economopoulos /*
19598892ea20SAggelos Economopoulos  * copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
19608892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
19618892ea20SAggelos Economopoulos  * pio handler in the nic.   We re-write the first segment's low
19628892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
19638892ea20SAggelos Economopoulos  * in a burst
19648892ea20SAggelos Economopoulos  */
1965ddbf91b7SSepherosa Ziehau static __inline void
19668892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
19678892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
19688892ea20SAggelos Economopoulos {
19698892ea20SAggelos Economopoulos 	uint32_t low;
19708892ea20SAggelos Economopoulos 
19718892ea20SAggelos Economopoulos 	low = src->addr_low;
19728892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
19738892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
19748892ea20SAggelos Economopoulos 	wmb();
19758892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
19768892ea20SAggelos Economopoulos 	wmb();
19778892ea20SAggelos Economopoulos 	src->addr_low = low;
19788892ea20SAggelos Economopoulos 	dst->addr_low = low;
19798892ea20SAggelos Economopoulos 	wmb();
19808892ea20SAggelos Economopoulos }
19818892ea20SAggelos Economopoulos 
19828892ea20SAggelos Economopoulos static int
19838ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
19848ebf015eSSepherosa Ziehau     boolean_t init)
19858892ea20SAggelos Economopoulos {
19868892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
19878892ea20SAggelos Economopoulos 	struct mbuf *m;
1988363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
19898892ea20SAggelos Economopoulos 
19908ebf015eSSepherosa Ziehau 	mflag = MB_DONTWAIT;
19918ebf015eSSepherosa Ziehau 	if (__predict_false(init))
19928ebf015eSSepherosa Ziehau 		mflag = MB_WAIT;
19938ebf015eSSepherosa Ziehau 
19948ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
19958892ea20SAggelos Economopoulos 	if (m == NULL) {
19968892ea20SAggelos Economopoulos 		rx->alloc_fail++;
19978892ea20SAggelos Economopoulos 		err = ENOBUFS;
19988ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
19998ebf015eSSepherosa Ziehau 			/*
20008ebf015eSSepherosa Ziehau 			 * During initialization, there
20018ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20028ebf015eSSepherosa Ziehau 			 */
20038ebf015eSSepherosa Ziehau 			return err;
20048ebf015eSSepherosa Ziehau 		}
20058892ea20SAggelos Economopoulos 		goto done;
20068892ea20SAggelos Economopoulos 	}
20072823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20088ebf015eSSepherosa Ziehau 
20097d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20107d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20118892ea20SAggelos Economopoulos 	if (err != 0) {
20128ebf015eSSepherosa Ziehau 		m_freem(m);
20138ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20148ebf015eSSepherosa Ziehau 			/*
20158ebf015eSSepherosa Ziehau 			 * During initialization, there
20168ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20178ebf015eSSepherosa Ziehau 			 */
20188ebf015eSSepherosa Ziehau 			return err;
20198ebf015eSSepherosa Ziehau 		}
20208892ea20SAggelos Economopoulos 		goto done;
20218892ea20SAggelos Economopoulos 	}
20228ebf015eSSepherosa Ziehau 
20238892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20248ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20258ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20268892ea20SAggelos Economopoulos 
20278892ea20SAggelos Economopoulos done:
20288892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20298892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20308892ea20SAggelos Economopoulos 	return err;
20318892ea20SAggelos Economopoulos }
20328892ea20SAggelos Economopoulos 
20338892ea20SAggelos Economopoulos static int
2034363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2035363b44f8SSepherosa Ziehau     boolean_t init)
20368892ea20SAggelos Economopoulos {
2037b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20388892ea20SAggelos Economopoulos 	struct mbuf *m;
2039363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2040363b44f8SSepherosa Ziehau 
2041363b44f8SSepherosa Ziehau 	mflag = MB_DONTWAIT;
2042363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2043363b44f8SSepherosa Ziehau 		mflag = MB_WAIT;
20448892ea20SAggelos Economopoulos 
20458892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2046363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2047b9a8961fSSepherosa Ziehau 	else
2048363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
20498892ea20SAggelos Economopoulos 	if (m == NULL) {
20508892ea20SAggelos Economopoulos 		rx->alloc_fail++;
20518892ea20SAggelos Economopoulos 		err = ENOBUFS;
2052363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2053363b44f8SSepherosa Ziehau 			/*
2054363b44f8SSepherosa Ziehau 			 * During initialization, there
2055363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2056363b44f8SSepherosa Ziehau 			 */
2057363b44f8SSepherosa Ziehau 			return err;
2058363b44f8SSepherosa Ziehau 		}
20598892ea20SAggelos Economopoulos 		goto done;
20608892ea20SAggelos Economopoulos 	}
20612823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = rx->mlen;
2062b9a8961fSSepherosa Ziehau 
20637d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2064b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20658892ea20SAggelos Economopoulos 	if (err != 0) {
2066363b44f8SSepherosa Ziehau 		m_freem(m);
2067363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2068363b44f8SSepherosa Ziehau 			/*
2069363b44f8SSepherosa Ziehau 			 * During initialization, there
2070363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2071363b44f8SSepherosa Ziehau 			 */
2072363b44f8SSepherosa Ziehau 			return err;
2073363b44f8SSepherosa Ziehau 		}
20748892ea20SAggelos Economopoulos 		goto done;
20758892ea20SAggelos Economopoulos 	}
2076b9a8961fSSepherosa Ziehau 
20778892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2078363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2079363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20808892ea20SAggelos Economopoulos 
20818892ea20SAggelos Economopoulos done:
2082b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2083b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20848892ea20SAggelos Economopoulos 	return err;
20858892ea20SAggelos Economopoulos }
20868892ea20SAggelos Economopoulos 
20878892ea20SAggelos Economopoulos /*
20888892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
20898892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
20908892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
20918892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
20928892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
20938892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
20948892ea20SAggelos Economopoulos  */
209552cf8dfcSSepherosa Ziehau static __inline uint16_t
20968892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
20978892ea20SAggelos Economopoulos {
209852cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
209952cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21008892ea20SAggelos Economopoulos 	uint16_t c;
21018892ea20SAggelos Economopoulos 
210252cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21038892ea20SAggelos Economopoulos 
210452cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21058892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21068892ea20SAggelos Economopoulos 		return 1;
210752cf8dfcSSepherosa Ziehau 
210852cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
210952cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21108892ea20SAggelos Economopoulos 		return 1;
211152cf8dfcSSepherosa Ziehau 
21128892ea20SAggelos Economopoulos #ifdef INET
21138892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21148892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21158892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21168892ea20SAggelos Economopoulos #else
21178892ea20SAggelos Economopoulos 	c = 1;
21188892ea20SAggelos Economopoulos #endif
21198892ea20SAggelos Economopoulos 	c ^= 0xffff;
212052cf8dfcSSepherosa Ziehau 	return c;
21218892ea20SAggelos Economopoulos }
21228892ea20SAggelos Economopoulos 
21238892ea20SAggelos Economopoulos static void
21248892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21258892ea20SAggelos Economopoulos {
21268892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21278892ea20SAggelos Economopoulos 	uint32_t partial;
21288892ea20SAggelos Economopoulos 
21298892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21308892ea20SAggelos Economopoulos 
21318892ea20SAggelos Economopoulos 	/*
213252cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
213352cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21348892ea20SAggelos Economopoulos 	 * header.
21358892ea20SAggelos Economopoulos 	 */
21368892ea20SAggelos Economopoulos 
213752cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21388892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21398892ea20SAggelos Economopoulos 
214052cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
214152cf8dfcSSepherosa Ziehau 	*csum += ~partial;
214252cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
214352cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
214452cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
214552cf8dfcSSepherosa Ziehau 
214652cf8dfcSSepherosa Ziehau 	/*
214752cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
214852cf8dfcSSepherosa Ziehau 	 * later consumers expect this
214952cf8dfcSSepherosa Ziehau 	 */
21508892ea20SAggelos Economopoulos 	*csum = htons(*csum);
21518892ea20SAggelos Economopoulos 
21528892ea20SAggelos Economopoulos 	/* save the tag */
2153b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
21548892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
21558892ea20SAggelos Economopoulos 
21568892ea20SAggelos Economopoulos 	/*
21578892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
21588892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
21598892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
21608892ea20SAggelos Economopoulos 	 * type field is already in place.
21618892ea20SAggelos Economopoulos 	 */
2162b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
21638892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2164b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
21658892ea20SAggelos Economopoulos }
21668892ea20SAggelos Economopoulos 
21678892ea20SAggelos Economopoulos 
216852cf8dfcSSepherosa Ziehau static __inline void
2169eda7db08SSepherosa Ziehau mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
21708892ea20SAggelos Economopoulos {
21718892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
21728892ea20SAggelos Economopoulos 	struct ifnet *ifp;
21738892ea20SAggelos Economopoulos 	struct mbuf *m;
217452cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
21758892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
21768892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
21778892ea20SAggelos Economopoulos 	int idx;
21788892ea20SAggelos Economopoulos 
21798892ea20SAggelos Economopoulos 	sc = ss->sc;
21808892ea20SAggelos Economopoulos 	ifp = sc->ifp;
21818892ea20SAggelos Economopoulos 	rx = &ss->rx_big;
218252cf8dfcSSepherosa Ziehau 
21838892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2184b9a8961fSSepherosa Ziehau 	rx->cnt++;
218552cf8dfcSSepherosa Ziehau 
218652cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
21878892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
218852cf8dfcSSepherosa Ziehau 
218952cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2190363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
219152cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2192d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
21938892ea20SAggelos Economopoulos 		return;
21948892ea20SAggelos Economopoulos 	}
21958892ea20SAggelos Economopoulos 
219652cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
21978892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
21988892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
21998892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22008892ea20SAggelos Economopoulos 
220152cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22028892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22038892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22048892ea20SAggelos Economopoulos 
220552cf8dfcSSepherosa Ziehau 	/*
220652cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
220752cf8dfcSSepherosa Ziehau 	 * aligned
220852cf8dfcSSepherosa Ziehau 	 */
22098892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22108892ea20SAggelos Economopoulos 
22118892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22128892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
221352cf8dfcSSepherosa Ziehau 
22148892ea20SAggelos Economopoulos 	ss->ipackets++;
221552cf8dfcSSepherosa Ziehau 
221652cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
221752cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22188892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
221952cf8dfcSSepherosa Ziehau 
222052cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
222189d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
222252cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
222389d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22248892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
222589d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
222689d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22278892ea20SAggelos Economopoulos 	}
2228eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
22298892ea20SAggelos Economopoulos }
22308892ea20SAggelos Economopoulos 
223152cf8dfcSSepherosa Ziehau static __inline void
2232eda7db08SSepherosa Ziehau mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, uint32_t csum)
22338892ea20SAggelos Economopoulos {
22348892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
22358892ea20SAggelos Economopoulos 	struct ifnet *ifp;
223652cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22378892ea20SAggelos Economopoulos 	struct mbuf *m;
22388892ea20SAggelos Economopoulos 	mxge_rx_ring_t *rx;
22398892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22408892ea20SAggelos Economopoulos 	int idx;
22418892ea20SAggelos Economopoulos 
22428892ea20SAggelos Economopoulos 	sc = ss->sc;
22438892ea20SAggelos Economopoulos 	ifp = sc->ifp;
22448892ea20SAggelos Economopoulos 	rx = &ss->rx_small;
224552cf8dfcSSepherosa Ziehau 
22468892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22478892ea20SAggelos Economopoulos 	rx->cnt++;
224852cf8dfcSSepherosa Ziehau 
224952cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22508892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
225152cf8dfcSSepherosa Ziehau 
225252cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22538ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
225452cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2255d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22568892ea20SAggelos Economopoulos 		return;
22578892ea20SAggelos Economopoulos 	}
22588892ea20SAggelos Economopoulos 
225952cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22608892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22618892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22628892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22638892ea20SAggelos Economopoulos 
226452cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22658892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22668892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22678892ea20SAggelos Economopoulos 
226852cf8dfcSSepherosa Ziehau 	/*
226952cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
227052cf8dfcSSepherosa Ziehau 	 * aligned
227152cf8dfcSSepherosa Ziehau 	 */
22728892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22738892ea20SAggelos Economopoulos 
22748892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22758892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
227652cf8dfcSSepherosa Ziehau 
22778892ea20SAggelos Economopoulos 	ss->ipackets++;
227852cf8dfcSSepherosa Ziehau 
227952cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
228052cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22818892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
228252cf8dfcSSepherosa Ziehau 
228352cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
228489d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
228552cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
228689d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22878892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
228889d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
228989d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22908892ea20SAggelos Economopoulos 	}
2291eda7db08SSepherosa Ziehau 	ifp->if_input(ifp, m);
22928892ea20SAggelos Economopoulos }
22938892ea20SAggelos Economopoulos 
229452cf8dfcSSepherosa Ziehau static __inline void
22958892ea20SAggelos Economopoulos mxge_clean_rx_done(struct mxge_slice_state *ss)
22968892ea20SAggelos Economopoulos {
22978892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
22988892ea20SAggelos Economopoulos 
22998892ea20SAggelos Economopoulos 	while (rx_done->entry[rx_done->idx].length != 0) {
230052cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
230152cf8dfcSSepherosa Ziehau 
23028892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23038892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
230452cf8dfcSSepherosa Ziehau 
23058892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
230652cf8dfcSSepherosa Ziehau 
23078892ea20SAggelos Economopoulos 		if (length <= (MHLEN - MXGEFW_PAD))
2308eda7db08SSepherosa Ziehau 			mxge_rx_done_small(ss, length, checksum);
23098892ea20SAggelos Economopoulos 		else
2310eda7db08SSepherosa Ziehau 			mxge_rx_done_big(ss, length, checksum);
231152cf8dfcSSepherosa Ziehau 
23128892ea20SAggelos Economopoulos 		rx_done->cnt++;
23138892ea20SAggelos Economopoulos 		rx_done->idx = rx_done->cnt & rx_done->mask;
23148892ea20SAggelos Economopoulos 	}
23158892ea20SAggelos Economopoulos }
23168892ea20SAggelos Economopoulos 
2317ddbf91b7SSepherosa Ziehau static __inline void
23188892ea20SAggelos Economopoulos mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx)
23198892ea20SAggelos Economopoulos {
23208892ea20SAggelos Economopoulos 	struct ifnet *ifp;
23218892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
23228892ea20SAggelos Economopoulos 
23238892ea20SAggelos Economopoulos 	tx = &ss->tx;
23248892ea20SAggelos Economopoulos 	ifp = ss->sc->ifp;
2325cd0543ffSAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
232666e7a0e8SSepherosa Ziehau 
23278892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
232866e7a0e8SSepherosa Ziehau 		struct mbuf *m;
232966e7a0e8SSepherosa Ziehau 		int idx;
233066e7a0e8SSepherosa Ziehau 
23318892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23328892ea20SAggelos Economopoulos 		tx->done++;
233366e7a0e8SSepherosa Ziehau 
23348892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
233566e7a0e8SSepherosa Ziehau 		/*
233666e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
233766e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
233866e7a0e8SSepherosa Ziehau 		 */
23398892ea20SAggelos Economopoulos 		if (m != NULL) {
23408892ea20SAggelos Economopoulos 			ss->opackets++;
23418892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
234266e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23438892ea20SAggelos Economopoulos 			m_freem(m);
23448892ea20SAggelos Economopoulos 		}
23458892ea20SAggelos Economopoulos 		if (tx->info[idx].flag) {
23468892ea20SAggelos Economopoulos 			tx->info[idx].flag = 0;
23478892ea20SAggelos Economopoulos 			tx->pkt_done++;
23488892ea20SAggelos Economopoulos 		}
23498892ea20SAggelos Economopoulos 	}
23508892ea20SAggelos Economopoulos 
235166e7a0e8SSepherosa Ziehau 	/*
235266e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
235366e7a0e8SSepherosa Ziehau 	 * its OK to send packets
235466e7a0e8SSepherosa Ziehau 	 */
235589d55360SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 4)
23569ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
235789d55360SSepherosa Ziehau 
235889d55360SSepherosa Ziehau 	if (!ifq_is_empty(&ifp->if_snd))
235989d55360SSepherosa Ziehau 		if_devstart(ifp);
236089d55360SSepherosa Ziehau 
23618892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
23628892ea20SAggelos Economopoulos 	if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) {
23638892ea20SAggelos Economopoulos 		/* let the NIC stop polling this queue, since there
23648892ea20SAggelos Economopoulos 		 * are no more transmits pending */
23658892ea20SAggelos Economopoulos 		if (tx->req == tx->done) {
23668892ea20SAggelos Economopoulos 			*tx->send_stop = 1;
23678892ea20SAggelos Economopoulos 			tx->queue_active = 0;
23688892ea20SAggelos Economopoulos 			tx->deactivate++;
23698892ea20SAggelos Economopoulos 			wmb();
23708892ea20SAggelos Economopoulos 		}
23718892ea20SAggelos Economopoulos 	}
23728892ea20SAggelos Economopoulos #endif
23738892ea20SAggelos Economopoulos }
23748892ea20SAggelos Economopoulos 
237589d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
23768892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
23778892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
23788892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
23798892ea20SAggelos Economopoulos 	{0,		(1 << 5),	"10GBASE-ER"},
23808892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
23818892ea20SAggelos Economopoulos 	{0,		(1 << 3),	"10GBASE-SW"},
23828892ea20SAggelos Economopoulos 	{0,		(1 << 2),	"10GBASE-LW"},
23838892ea20SAggelos Economopoulos 	{0,		(1 << 1),	"10GBASE-EW"},
23848892ea20SAggelos Economopoulos 	{0,		(1 << 0),	"Reserved"}
23858892ea20SAggelos Economopoulos };
238689d55360SSepherosa Ziehau 
238789d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
238889d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
23898892ea20SAggelos Economopoulos 	{0,		(1 << 7),	"Reserved"},
23908892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
23918892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
239289d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
239389d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
23948892ea20SAggelos Economopoulos };
23958892ea20SAggelos Economopoulos 
23968892ea20SAggelos Economopoulos static void
239789d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
23988892ea20SAggelos Economopoulos {
23997cc92483SSepherosa Ziehau 	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 0, NULL);
240089d55360SSepherosa Ziehau 	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type);
240189d55360SSepherosa Ziehau 	sc->current_media = media_type;
240289d55360SSepherosa Ziehau 	sc->media.ifm_media = sc->media.ifm_cur->ifm_media;
24038892ea20SAggelos Economopoulos }
24048892ea20SAggelos Economopoulos 
24058892ea20SAggelos Economopoulos static void
240689d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24078892ea20SAggelos Economopoulos {
2408c7431c78SSepherosa Ziehau 	const char *ptr;
240989d55360SSepherosa Ziehau 	int i;
24108892ea20SAggelos Economopoulos 
241189d55360SSepherosa Ziehau 	ifmedia_removeall(&sc->media);
241289d55360SSepherosa Ziehau 	mxge_media_set(sc, IFM_AUTO);
24138892ea20SAggelos Economopoulos 
24148892ea20SAggelos Economopoulos 	/*
24158892ea20SAggelos Economopoulos 	 * parse the product code to deterimine the interface type
24168892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24178892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24188892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24198892ea20SAggelos Economopoulos 	 */
24208892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24218892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2422af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
242389d55360SSepherosa Ziehau 		return;
24248892ea20SAggelos Economopoulos 	}
24258892ea20SAggelos Economopoulos 
24268892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
242789d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24288892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2429af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24308892ea20SAggelos Economopoulos 			return;
24318892ea20SAggelos Economopoulos 		}
24328892ea20SAggelos Economopoulos 	}
243389d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24348892ea20SAggelos Economopoulos 		/* -C is CX4 */
243589d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
243689d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
243789d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24388892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
243989d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2440af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24418892ea20SAggelos Economopoulos 		/* FreeBSD has no media type for Quad ribbon fiber */
244289d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
244389d55360SSepherosa Ziehau 		/* -R is XFP */
244489d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
244589d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
244689d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
244789d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
244889d55360SSepherosa Ziehau 	} else {
2449af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
245089d55360SSepherosa Ziehau 	}
24518892ea20SAggelos Economopoulos }
24528892ea20SAggelos Economopoulos 
245389d55360SSepherosa Ziehau /*
245489d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
245589d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
245689d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
245789d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
245889d55360SSepherosa Ziehau  * than in the interrupt handler itself.
245989d55360SSepherosa Ziehau  */
246089d55360SSepherosa Ziehau static void
246189d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
246289d55360SSepherosa Ziehau {
246389d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
24647cc92483SSepherosa Ziehau 	const char *cage_type;
246589d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
246689d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
246789d55360SSepherosa Ziehau 	uint32_t byte;
246889d55360SSepherosa Ziehau 
246989d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
247089d55360SSepherosa Ziehau 
247189d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
24728892ea20SAggelos Economopoulos 		/* -R is XFP */
24738892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
24747cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_xfp_media_types) /
247589d55360SSepherosa Ziehau 		    sizeof(mxge_xfp_media_types[0]);
24768892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
24778892ea20SAggelos Economopoulos 		cage_type = "XFP";
247889d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
24798892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
24808892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
24817cc92483SSepherosa Ziehau 		mxge_media_type_entries = sizeof(mxge_sfp_media_types) /
248289d55360SSepherosa Ziehau 		    sizeof(mxge_sfp_media_types[0]);
24838892ea20SAggelos Economopoulos 		cage_type = "SFP+";
24848892ea20SAggelos Economopoulos 		byte = 3;
248589d55360SSepherosa Ziehau 	} else {
248689d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
24878892ea20SAggelos Economopoulos 		return;
24888892ea20SAggelos Economopoulos 	}
24898892ea20SAggelos Economopoulos 
24908892ea20SAggelos Economopoulos 	/*
24918892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
24928892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
24938892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
24948892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
24958892ea20SAggelos Economopoulos 	 * a millisecond
24968892ea20SAggelos Economopoulos 	 */
24978892ea20SAggelos Economopoulos 
24988892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
24998892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25008892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
25017cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2502af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read XFP\n");
25037cc92483SSepherosa Ziehau 	if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2504af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
25057cc92483SSepherosa Ziehau 	if (err != MXGEFW_CMD_OK)
25068892ea20SAggelos Economopoulos 		return;
25078892ea20SAggelos Economopoulos 
25087cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25098892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25108892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25117cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25128892ea20SAggelos Economopoulos 		DELAY(1000);
25138892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25148892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25158892ea20SAggelos Economopoulos 	}
25168892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2517af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25188892ea20SAggelos Economopoulos 		    cage_type, err, ms);
25198892ea20SAggelos Economopoulos 		return;
25208892ea20SAggelos Economopoulos 	}
25218892ea20SAggelos Economopoulos 
25228892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25237cc92483SSepherosa Ziehau 		if (bootverbose) {
2524af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25258892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25267cc92483SSepherosa Ziehau 		}
252789d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
252889d55360SSepherosa Ziehau 			mxge_media_init(sc);
252989d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
253089d55360SSepherosa Ziehau 		}
25318892ea20SAggelos Economopoulos 		return;
25328892ea20SAggelos Economopoulos 	}
25338892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25348892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25357cc92483SSepherosa Ziehau 			if (bootverbose) {
2536af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25378892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25387cc92483SSepherosa Ziehau 			}
25398892ea20SAggelos Economopoulos 
254089d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
254189d55360SSepherosa Ziehau 				mxge_media_init(sc);
254289d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
254389d55360SSepherosa Ziehau 			}
25448892ea20SAggelos Economopoulos 			return;
25458892ea20SAggelos Economopoulos 		}
25468892ea20SAggelos Economopoulos 	}
25477cc92483SSepherosa Ziehau 	if (bootverbose) {
2548af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
25497cc92483SSepherosa Ziehau 		    cmd.data0);
25507cc92483SSepherosa Ziehau 	}
25518892ea20SAggelos Economopoulos }
25528892ea20SAggelos Economopoulos 
25538892ea20SAggelos Economopoulos static void
25548892ea20SAggelos Economopoulos mxge_intr(void *arg)
25558892ea20SAggelos Economopoulos {
25568892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss = arg;
25578892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
25588892ea20SAggelos Economopoulos 	mcp_irq_data_t *stats = ss->fw_stats;
25598892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx = &ss->tx;
25608892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done = &ss->rx_done;
25618892ea20SAggelos Economopoulos 	uint32_t send_done_count;
25628892ea20SAggelos Economopoulos 	uint8_t valid;
25638892ea20SAggelos Economopoulos 
25648892ea20SAggelos Economopoulos 
25658892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
25668892ea20SAggelos Economopoulos 	/* an interrupt on a non-zero slice is implicitly valid
25678892ea20SAggelos Economopoulos 	   since MSI-X irqs are not shared */
25688892ea20SAggelos Economopoulos 	if (ss != sc->ss) {
25698892ea20SAggelos Economopoulos 		mxge_clean_rx_done(ss);
25708892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
25718892ea20SAggelos Economopoulos 		return;
25728892ea20SAggelos Economopoulos 	}
25738892ea20SAggelos Economopoulos #endif
25748892ea20SAggelos Economopoulos 
25758892ea20SAggelos Economopoulos 	/* make sure the DMA has finished */
25768892ea20SAggelos Economopoulos 	if (!stats->valid) {
25778892ea20SAggelos Economopoulos 		return;
25788892ea20SAggelos Economopoulos 	}
25798892ea20SAggelos Economopoulos 	valid = stats->valid;
25808892ea20SAggelos Economopoulos 
258189d55360SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_LEGACY) {
25828892ea20SAggelos Economopoulos 		/* lower legacy IRQ  */
25838892ea20SAggelos Economopoulos 		*sc->irq_deassert = 0;
25848892ea20SAggelos Economopoulos 		if (!mxge_deassert_wait)
25858892ea20SAggelos Economopoulos 			/* don't wait for conf. that irq is low */
25868892ea20SAggelos Economopoulos 			stats->valid = 0;
25878892ea20SAggelos Economopoulos 	} else {
25888892ea20SAggelos Economopoulos 		stats->valid = 0;
25898892ea20SAggelos Economopoulos 	}
25908892ea20SAggelos Economopoulos 
25918892ea20SAggelos Economopoulos 	/* loop while waiting for legacy irq deassertion */
25928892ea20SAggelos Economopoulos 	do {
25938892ea20SAggelos Economopoulos 		/* check for transmit completes and receives */
25948892ea20SAggelos Economopoulos 		send_done_count = be32toh(stats->send_done_count);
25958892ea20SAggelos Economopoulos 		while ((send_done_count != tx->pkt_done) ||
25968892ea20SAggelos Economopoulos 		       (rx_done->entry[rx_done->idx].length != 0)) {
25978892ea20SAggelos Economopoulos 			if (send_done_count != tx->pkt_done)
25988892ea20SAggelos Economopoulos 				mxge_tx_done(ss, (int)send_done_count);
25998892ea20SAggelos Economopoulos 			mxge_clean_rx_done(ss);
26008892ea20SAggelos Economopoulos 			send_done_count = be32toh(stats->send_done_count);
26018892ea20SAggelos Economopoulos 		}
260289d55360SSepherosa Ziehau 		if (sc->irq_type == PCI_INTR_TYPE_LEGACY && mxge_deassert_wait)
26038892ea20SAggelos Economopoulos 			wmb();
26048892ea20SAggelos Economopoulos 	} while (*((volatile uint8_t *) &stats->valid));
26058892ea20SAggelos Economopoulos 
26068892ea20SAggelos Economopoulos 	/* fw link & error stats meaningful only on the first slice */
26078892ea20SAggelos Economopoulos 	if (__predict_false((ss == sc->ss) && stats->stats_updated)) {
26088892ea20SAggelos Economopoulos 		if (sc->link_state != stats->link_up) {
26098892ea20SAggelos Economopoulos 			sc->link_state = stats->link_up;
26108892ea20SAggelos Economopoulos 			if (sc->link_state) {
261173a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_UP;
261273a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26137cc92483SSepherosa Ziehau 				if (bootverbose)
26148892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link up\n");
26158892ea20SAggelos Economopoulos 			} else {
261673a22abeSAggelos Economopoulos 				sc->ifp->if_link_state = LINK_STATE_DOWN;
261773a22abeSAggelos Economopoulos 				if_link_state_change(sc->ifp);
26187cc92483SSepherosa Ziehau 				if (bootverbose)
26198892ea20SAggelos Economopoulos 					device_printf(sc->dev, "link down\n");
26208892ea20SAggelos Economopoulos 			}
26218892ea20SAggelos Economopoulos 			sc->need_media_probe = 1;
26228892ea20SAggelos Economopoulos 		}
26238892ea20SAggelos Economopoulos 		if (sc->rdma_tags_available !=
26248892ea20SAggelos Economopoulos 		    be32toh(stats->rdma_tags_available)) {
26258892ea20SAggelos Economopoulos 			sc->rdma_tags_available =
26268892ea20SAggelos Economopoulos 				be32toh(stats->rdma_tags_available);
26278892ea20SAggelos Economopoulos 			device_printf(sc->dev, "RDMA timed out! %d tags "
26288892ea20SAggelos Economopoulos 				      "left\n", sc->rdma_tags_available);
26298892ea20SAggelos Economopoulos 		}
26308892ea20SAggelos Economopoulos 
26318892ea20SAggelos Economopoulos 		if (stats->link_down) {
26328892ea20SAggelos Economopoulos 			sc->down_cnt += stats->link_down;
26338892ea20SAggelos Economopoulos 			sc->link_state = 0;
2634f0115d64SAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
2635f0115d64SAggelos Economopoulos 			if_link_state_change(sc->ifp);
26368892ea20SAggelos Economopoulos 		}
26378892ea20SAggelos Economopoulos 	}
26388892ea20SAggelos Economopoulos 
26398892ea20SAggelos Economopoulos 	/* check to see if we have rx token to pass back */
26408892ea20SAggelos Economopoulos 	if (valid & 0x1)
26418892ea20SAggelos Economopoulos 	    *ss->irq_claim = be32toh(3);
26428892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
26438892ea20SAggelos Economopoulos }
26448892ea20SAggelos Economopoulos 
26458892ea20SAggelos Economopoulos static void
26468892ea20SAggelos Economopoulos mxge_init(void *arg)
26478892ea20SAggelos Economopoulos {
264889d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
264989d55360SSepherosa Ziehau 
265089d55360SSepherosa Ziehau 	ASSERT_SERIALIZED(sc->ifp->if_serializer);
265189d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
265289d55360SSepherosa Ziehau 		mxge_open(sc);
26538892ea20SAggelos Economopoulos }
26548892ea20SAggelos Economopoulos 
26558892ea20SAggelos Economopoulos static void
26568892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
26578892ea20SAggelos Economopoulos {
26588892ea20SAggelos Economopoulos 	int i;
26598892ea20SAggelos Economopoulos 
26608892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
26618892ea20SAggelos Economopoulos 		if (ss->rx_big.info[i].m == NULL)
26628892ea20SAggelos Economopoulos 			continue;
2663*4e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->rx_big.dmat, ss->rx_big.info[i].map);
26648892ea20SAggelos Economopoulos 		m_freem(ss->rx_big.info[i].m);
26658892ea20SAggelos Economopoulos 		ss->rx_big.info[i].m = NULL;
26668892ea20SAggelos Economopoulos 	}
26678892ea20SAggelos Economopoulos 
26688892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
26698892ea20SAggelos Economopoulos 		if (ss->rx_small.info[i].m == NULL)
26708892ea20SAggelos Economopoulos 			continue;
2671*4e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->rx_small.dmat, ss->rx_small.info[i].map);
26728892ea20SAggelos Economopoulos 		m_freem(ss->rx_small.info[i].m);
26738892ea20SAggelos Economopoulos 		ss->rx_small.info[i].m = NULL;
26748892ea20SAggelos Economopoulos 	}
26758892ea20SAggelos Economopoulos 
2676*4e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
26778892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
26788892ea20SAggelos Economopoulos 		return;
26798892ea20SAggelos Economopoulos 
26808892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
26818892ea20SAggelos Economopoulos 		ss->tx.info[i].flag = 0;
26828892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
26838892ea20SAggelos Economopoulos 			continue;
2684*4e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
26858892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
26868892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
26878892ea20SAggelos Economopoulos 	}
26888892ea20SAggelos Economopoulos }
26898892ea20SAggelos Economopoulos 
26908892ea20SAggelos Economopoulos static void
26918892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
26928892ea20SAggelos Economopoulos {
26938892ea20SAggelos Economopoulos 	int slice;
26948892ea20SAggelos Economopoulos 
26958892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
26968892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
26978892ea20SAggelos Economopoulos }
26988892ea20SAggelos Economopoulos 
26998892ea20SAggelos Economopoulos static void
27008892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
27018892ea20SAggelos Economopoulos {
27028892ea20SAggelos Economopoulos 	int i;
27038892ea20SAggelos Economopoulos 
2704798c3369SSepherosa Ziehau 	if (ss->rx_done.entry != NULL) {
27058892ea20SAggelos Economopoulos 		mxge_dma_free(&ss->rx_done.dma);
27068892ea20SAggelos Economopoulos 		ss->rx_done.entry = NULL;
2707798c3369SSepherosa Ziehau 	}
27088892ea20SAggelos Economopoulos 
2709798c3369SSepherosa Ziehau 	if (ss->tx.req_bytes != NULL) {
2710d777b84fSAggelos Economopoulos 		kfree(ss->tx.req_bytes, M_DEVBUF);
27118892ea20SAggelos Economopoulos 		ss->tx.req_bytes = NULL;
2712798c3369SSepherosa Ziehau 	}
27138892ea20SAggelos Economopoulos 
2714798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2715d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
27168892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2717798c3369SSepherosa Ziehau 	}
27188892ea20SAggelos Economopoulos 
2719798c3369SSepherosa Ziehau 	if (ss->rx_small.shadow != NULL) {
2720d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.shadow, M_DEVBUF);
27218892ea20SAggelos Economopoulos 		ss->rx_small.shadow = NULL;
2722798c3369SSepherosa Ziehau 	}
27238892ea20SAggelos Economopoulos 
2724798c3369SSepherosa Ziehau 	if (ss->rx_big.shadow != NULL) {
2725d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.shadow, M_DEVBUF);
27268892ea20SAggelos Economopoulos 		ss->rx_big.shadow = NULL;
2727798c3369SSepherosa Ziehau 	}
27288892ea20SAggelos Economopoulos 
27298892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
27308892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
27318892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
27328892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
27338892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
27348892ea20SAggelos Economopoulos 			}
27358892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
27368892ea20SAggelos Economopoulos 		}
2737d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
27388892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2739798c3369SSepherosa Ziehau 	}
27408892ea20SAggelos Economopoulos 
27418892ea20SAggelos Economopoulos 	if (ss->rx_small.info != NULL) {
27428892ea20SAggelos Economopoulos 		if (ss->rx_small.dmat != NULL) {
27438892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_small.mask; i++) {
27448892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_small.dmat,
27458892ea20SAggelos Economopoulos 				    ss->rx_small.info[i].map);
27468892ea20SAggelos Economopoulos 			}
27478892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_small.dmat,
27488892ea20SAggelos Economopoulos 			    ss->rx_small.extra_map);
27498892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_small.dmat);
27508892ea20SAggelos Economopoulos 		}
2751d777b84fSAggelos Economopoulos 		kfree(ss->rx_small.info, M_DEVBUF);
27528892ea20SAggelos Economopoulos 		ss->rx_small.info = NULL;
2753798c3369SSepherosa Ziehau 	}
27548892ea20SAggelos Economopoulos 
27558892ea20SAggelos Economopoulos 	if (ss->rx_big.info != NULL) {
27568892ea20SAggelos Economopoulos 		if (ss->rx_big.dmat != NULL) {
27578892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->rx_big.mask; i++) {
27588892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->rx_big.dmat,
27598892ea20SAggelos Economopoulos 				    ss->rx_big.info[i].map);
27608892ea20SAggelos Economopoulos 			}
27618892ea20SAggelos Economopoulos 			bus_dmamap_destroy(ss->rx_big.dmat,
27628892ea20SAggelos Economopoulos 			    ss->rx_big.extra_map);
27638892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->rx_big.dmat);
27648892ea20SAggelos Economopoulos 		}
2765d777b84fSAggelos Economopoulos 		kfree(ss->rx_big.info, M_DEVBUF);
27668892ea20SAggelos Economopoulos 		ss->rx_big.info = NULL;
27678892ea20SAggelos Economopoulos 	}
2768798c3369SSepherosa Ziehau }
27698892ea20SAggelos Economopoulos 
27708892ea20SAggelos Economopoulos static void
27718892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
27728892ea20SAggelos Economopoulos {
27738892ea20SAggelos Economopoulos 	int slice;
27748892ea20SAggelos Economopoulos 
2775798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2776798c3369SSepherosa Ziehau 		return;
2777798c3369SSepherosa Ziehau 
27788892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
27798892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
27808892ea20SAggelos Economopoulos }
27818892ea20SAggelos Economopoulos 
27828892ea20SAggelos Economopoulos static int
27838892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
27848892ea20SAggelos Economopoulos     int tx_ring_entries)
27858892ea20SAggelos Economopoulos {
27868892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
27878892ea20SAggelos Economopoulos 	size_t bytes;
27888892ea20SAggelos Economopoulos 	int err, i;
27898892ea20SAggelos Economopoulos 
27907cc92483SSepherosa Ziehau 	/*
27917cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
27927cc92483SSepherosa Ziehau 	 */
27938892ea20SAggelos Economopoulos 
27948892ea20SAggelos Economopoulos 	ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;
27958892ea20SAggelos Economopoulos 	ss->rx_done.mask = (2 * rx_ring_entries) - 1;
27968892ea20SAggelos Economopoulos 
27977cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
27988892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow);
2799d777b84fSAggelos Economopoulos 	ss->rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28008892ea20SAggelos Economopoulos 
28018892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow);
2802d777b84fSAggelos Economopoulos 	ss->rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28038892ea20SAggelos Economopoulos 
28047cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
28058892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_small.info);
2806d777b84fSAggelos Economopoulos 	ss->rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28078892ea20SAggelos Economopoulos 
28088892ea20SAggelos Economopoulos 	bytes = rx_ring_entries * sizeof(*ss->rx_big.info);
2809d777b84fSAggelos Economopoulos 	ss->rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
28108892ea20SAggelos Economopoulos 
28117cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
28128892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28138892ea20SAggelos Economopoulos 				 1,			/* alignment */
28148892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28158892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28168892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28178892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
28188892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
28198892ea20SAggelos Economopoulos 				 1,			/* num segs */
28208892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
28217cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
28227cc92483SSepherosa Ziehau 				 			/* flags */
28238892ea20SAggelos Economopoulos 				 &ss->rx_small.dmat);	/* tag */
28248892ea20SAggelos Economopoulos 	if (err != 0) {
28258892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
28268892ea20SAggelos Economopoulos 		    err);
28273598cc14SSascha Wildner 		return err;
28288892ea20SAggelos Economopoulos 	}
28298892ea20SAggelos Economopoulos 
2830798c3369SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2831798c3369SSepherosa Ziehau 	    &ss->rx_small.extra_map);
2832798c3369SSepherosa Ziehau 	if (err != 0) {
2833798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
2834798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_small.dmat);
2835798c3369SSepherosa Ziehau 		ss->rx_small.dmat = NULL;
2836798c3369SSepherosa Ziehau 		return err;
2837798c3369SSepherosa Ziehau 	}
2838798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_small.mask; i++) {
2839798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_small.dmat, BUS_DMA_WAITOK,
2840798c3369SSepherosa Ziehau 		    &ss->rx_small.info[i].map);
2841798c3369SSepherosa Ziehau 		if (err != 0) {
2842798c3369SSepherosa Ziehau 			int j;
2843798c3369SSepherosa Ziehau 
2844798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
2845798c3369SSepherosa Ziehau 
2846798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2847798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_small.dmat,
2848798c3369SSepherosa Ziehau 				    ss->rx_small.info[j].map);
2849798c3369SSepherosa Ziehau 			}
2850798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_small.dmat,
2851798c3369SSepherosa Ziehau 			    ss->rx_small.extra_map);
2852798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_small.dmat);
2853798c3369SSepherosa Ziehau 			ss->rx_small.dmat = NULL;
2854798c3369SSepherosa Ziehau 			return err;
2855798c3369SSepherosa Ziehau 		}
2856798c3369SSepherosa Ziehau 	}
2857798c3369SSepherosa Ziehau 
28588892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
28598892ea20SAggelos Economopoulos 				 1,			/* alignment */
28608892ea20SAggelos Economopoulos 				 4096,			/* boundary */
28618892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
28628892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
28638892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
2864b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
2865b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
28668892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
28677cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
28687cc92483SSepherosa Ziehau 				 			/* flags */
28698892ea20SAggelos Economopoulos 				 &ss->rx_big.dmat);	/* tag */
28708892ea20SAggelos Economopoulos 	if (err != 0) {
28718892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
28728892ea20SAggelos Economopoulos 		    err);
28733598cc14SSascha Wildner 		return err;
28748892ea20SAggelos Economopoulos 	}
28757cc92483SSepherosa Ziehau 
28767cc92483SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
28778892ea20SAggelos Economopoulos 	    &ss->rx_big.extra_map);
28788892ea20SAggelos Economopoulos 	if (err != 0) {
28797cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
2880798c3369SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_big.dmat);
2881798c3369SSepherosa Ziehau 		ss->rx_big.dmat = NULL;
28823598cc14SSascha Wildner 		return err;
28838892ea20SAggelos Economopoulos 	}
2884798c3369SSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
2885798c3369SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_big.dmat, BUS_DMA_WAITOK,
2886798c3369SSepherosa Ziehau 		    &ss->rx_big.info[i].map);
2887798c3369SSepherosa Ziehau 		if (err != 0) {
2888798c3369SSepherosa Ziehau 			int j;
2889798c3369SSepherosa Ziehau 
2890798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
2891798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2892798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_big.dmat,
2893798c3369SSepherosa Ziehau 				    ss->rx_big.info[j].map);
2894798c3369SSepherosa Ziehau 			}
2895798c3369SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_big.dmat,
2896798c3369SSepherosa Ziehau 			    ss->rx_big.extra_map);
2897798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_big.dmat);
2898798c3369SSepherosa Ziehau 			ss->rx_big.dmat = NULL;
2899798c3369SSepherosa Ziehau 			return err;
2900798c3369SSepherosa Ziehau 		}
2901798c3369SSepherosa Ziehau 	}
29028892ea20SAggelos Economopoulos 
29037cc92483SSepherosa Ziehau 	/*
29047cc92483SSepherosa Ziehau 	 * Now allocate TX resources
29057cc92483SSepherosa Ziehau 	 */
29068892ea20SAggelos Economopoulos 
29078892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
29088892ea20SAggelos Economopoulos 	/* only use a single TX ring for now */
29098892ea20SAggelos Economopoulos 	if (ss != ss->sc->ss)
29108892ea20SAggelos Economopoulos 		return 0;
29118892ea20SAggelos Economopoulos #endif
29128892ea20SAggelos Economopoulos 
29138892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
29148892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
29158892ea20SAggelos Economopoulos 
29167cc92483SSepherosa Ziehau 	/* Allocate the tx request copy block XXX */
29177cc92483SSepherosa Ziehau 	bytes = 8 + sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
2918d777b84fSAggelos Economopoulos 	ss->tx.req_bytes = kmalloc(bytes, M_DEVBUF, M_WAITOK);
29197cc92483SSepherosa Ziehau 	/* Ensure req_list entries are aligned to 8 bytes */
29208892ea20SAggelos Economopoulos 	ss->tx.req_list = (mcp_kreq_ether_send_t *)
29218892ea20SAggelos Economopoulos 	    ((unsigned long)(ss->tx.req_bytes + 7) & ~7UL);
29228892ea20SAggelos Economopoulos 
29237cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
29248892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
29257cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
29268892ea20SAggelos Economopoulos 
29277cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
29288892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
2929d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
29308892ea20SAggelos Economopoulos 
29317cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
29328892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
29338892ea20SAggelos Economopoulos 				 1,			/* alignment */
29348892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
29358892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
29368892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
29378892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
29387cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
29397cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
29407cc92483SSepherosa Ziehau 				 			/* maxsize */
29418892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
29428892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
29437cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
29447cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
29458892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
29468892ea20SAggelos Economopoulos 	if (err != 0) {
29477cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
29483598cc14SSascha Wildner 		return err;
29498892ea20SAggelos Economopoulos 	}
29508892ea20SAggelos Economopoulos 
29517cc92483SSepherosa Ziehau 	/*
29527cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
29537cc92483SSepherosa Ziehau 	 */
29548892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
29557cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
29567cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
29578892ea20SAggelos Economopoulos 		if (err != 0) {
2958798c3369SSepherosa Ziehau 			int j;
2959798c3369SSepherosa Ziehau 
29607cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
2961798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
2962798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
2963798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
2964798c3369SSepherosa Ziehau 			}
2965798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
2966798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
29673598cc14SSascha Wildner 			return err;
29688892ea20SAggelos Economopoulos 		}
29698892ea20SAggelos Economopoulos 	}
29708892ea20SAggelos Economopoulos 	return 0;
29718892ea20SAggelos Economopoulos }
29728892ea20SAggelos Economopoulos 
29738892ea20SAggelos Economopoulos static int
29748892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
29758892ea20SAggelos Economopoulos {
29768892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
29778892ea20SAggelos Economopoulos 	int tx_ring_size;
29788892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
29798892ea20SAggelos Economopoulos 	int err, slice;
29808892ea20SAggelos Economopoulos 
29817cc92483SSepherosa Ziehau 	/* Get ring sizes */
29828892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
29838892ea20SAggelos Economopoulos 	if (err != 0) {
29848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
2985798c3369SSepherosa Ziehau 		return err;
29868892ea20SAggelos Economopoulos 	}
29877cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
29888892ea20SAggelos Economopoulos 
29898892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
29908892ea20SAggelos Economopoulos 	rx_ring_entries = sc->rx_ring_size / sizeof(mcp_dma_addr_t);
2991f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
2992f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
29938892ea20SAggelos Economopoulos 
29948892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
29958892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
29967cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
2997798c3369SSepherosa Ziehau 		if (err != 0) {
2998798c3369SSepherosa Ziehau 			device_printf(sc->dev,
2999798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3000798c3369SSepherosa Ziehau 			return err;
3001798c3369SSepherosa Ziehau 		}
30028892ea20SAggelos Economopoulos 	}
30038892ea20SAggelos Economopoulos 	return 0;
30048892ea20SAggelos Economopoulos }
30058892ea20SAggelos Economopoulos 
30068892ea20SAggelos Economopoulos static void
3007b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
30088892ea20SAggelos Economopoulos {
3009b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
30108892ea20SAggelos Economopoulos 
30118892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
30128892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3013b9a8961fSSepherosa Ziehau 	} else {
3014b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
30158892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
30168892ea20SAggelos Economopoulos 	}
30178892ea20SAggelos Economopoulos }
30188892ea20SAggelos Economopoulos 
30198892ea20SAggelos Economopoulos static int
3020b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
30218892ea20SAggelos Economopoulos {
30228892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
30238892ea20SAggelos Economopoulos 	int err, i, slice;
30248892ea20SAggelos Economopoulos 
3025308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
30268892ea20SAggelos Economopoulos 
3027308781adSSepherosa Ziehau 	/*
3028308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3029308781adSSepherosa Ziehau 	 */
30308892ea20SAggelos Economopoulos 	err = 0;
30318892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30328892ea20SAggelos Economopoulos 	/* We currently only send from the first slice */
30338892ea20SAggelos Economopoulos 	if (slice == 0) {
30348892ea20SAggelos Economopoulos #endif
30358892ea20SAggelos Economopoulos 		cmd.data0 = slice;
3036308781adSSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3037308781adSSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3038308781adSSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
30398892ea20SAggelos Economopoulos 		ss->tx.send_go = (volatile uint32_t *)
3040308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
30418892ea20SAggelos Economopoulos 		ss->tx.send_stop = (volatile uint32_t *)
3042308781adSSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
30438892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
30448892ea20SAggelos Economopoulos 	}
30458892ea20SAggelos Economopoulos #endif
3046308781adSSepherosa Ziehau 
30478892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3048308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
30498892ea20SAggelos Economopoulos 	ss->rx_small.lanai =
3050308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3051308781adSSepherosa Ziehau 
30528892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3053308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
30548892ea20SAggelos Economopoulos 	ss->rx_big.lanai =
3055308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
30568892ea20SAggelos Economopoulos 
30578892ea20SAggelos Economopoulos 	if (err != 0) {
3058308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
30598892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
30608892ea20SAggelos Economopoulos 		return EIO;
30618892ea20SAggelos Economopoulos 	}
30628892ea20SAggelos Economopoulos 
3063308781adSSepherosa Ziehau 	/*
3064308781adSSepherosa Ziehau 	 * Stock small receive ring
3065308781adSSepherosa Ziehau 	 */
30668892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_small.mask; i++) {
30678ebf015eSSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_small,
30688ebf015eSSepherosa Ziehau 		    ss->rx_small.info[i].map, i, TRUE);
30698892ea20SAggelos Economopoulos 		if (err) {
3070308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
3071308781adSSepherosa Ziehau 			    ss->rx_small.mask + 1);
30728892ea20SAggelos Economopoulos 			return ENOMEM;
30738892ea20SAggelos Economopoulos 		}
30748892ea20SAggelos Economopoulos 	}
3075308781adSSepherosa Ziehau 
3076308781adSSepherosa Ziehau 	/*
3077308781adSSepherosa Ziehau 	 * Stock big receive ring
3078308781adSSepherosa Ziehau 	 */
30798892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->rx_big.mask; i++) {
30808892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_low = 0xffffffff;
30818892ea20SAggelos Economopoulos 		ss->rx_big.shadow[i].addr_high = 0xffffffff;
30828892ea20SAggelos Economopoulos 	}
3083308781adSSepherosa Ziehau 
30848892ea20SAggelos Economopoulos 	ss->rx_big.cl_size = cl_size;
30858892ea20SAggelos Economopoulos 	ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN +
3086b915556eSAggelos Economopoulos 	    EVL_ENCAPLEN + MXGEFW_PAD;
3087308781adSSepherosa Ziehau 
3088b9a8961fSSepherosa Ziehau 	for (i = 0; i <= ss->rx_big.mask; i++) {
3089363b44f8SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_big,
3090363b44f8SSepherosa Ziehau 		    ss->rx_big.info[i].map, i, TRUE);
30918892ea20SAggelos Economopoulos 		if (err) {
3092308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
3093308781adSSepherosa Ziehau 			    ss->rx_big.mask + 1);
30948892ea20SAggelos Economopoulos 			return ENOMEM;
30958892ea20SAggelos Economopoulos 		}
30968892ea20SAggelos Economopoulos 	}
30978892ea20SAggelos Economopoulos 	return 0;
30988892ea20SAggelos Economopoulos }
30998892ea20SAggelos Economopoulos 
31008892ea20SAggelos Economopoulos static int
31018892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
31028892ea20SAggelos Economopoulos {
31036ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
31048892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3105b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
31068892ea20SAggelos Economopoulos 	bus_addr_t bus;
31078892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
31088892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
31098892ea20SAggelos Economopoulos 
31106ee6cba3SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
31116ee6cba3SSepherosa Ziehau 
31128892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
31136ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
31148892ea20SAggelos Economopoulos 
31158892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
31168892ea20SAggelos Economopoulos 	if (err != 0) {
31176ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
31188892ea20SAggelos Economopoulos 		return EIO;
31198892ea20SAggelos Economopoulos 	}
31208892ea20SAggelos Economopoulos 
31218892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
31226ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
31238892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
31246ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
31258892ea20SAggelos Economopoulos 
31266ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
31278892ea20SAggelos Economopoulos 		if (err != 0) {
31286ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
31298892ea20SAggelos Economopoulos 			return err;
31308892ea20SAggelos Economopoulos 		}
31318892ea20SAggelos Economopoulos 
31326ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
31338892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
31348892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
31358892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
31368892ea20SAggelos Economopoulos 
31378892ea20SAggelos Economopoulos 		cmd.data0 = 1;
31386ee6cba3SSepherosa Ziehau 		cmd.data1 = MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
31398892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
31408892ea20SAggelos Economopoulos 		if (err != 0) {
31416ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
31428892ea20SAggelos Economopoulos 			return err;
31438892ea20SAggelos Economopoulos 		}
31448892ea20SAggelos Economopoulos 	}
31458892ea20SAggelos Economopoulos 
314689d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
314789d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
314889d55360SSepherosa Ziehau 	if (err) {
31496ee6cba3SSepherosa Ziehau 		/*
31506ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
31516ee6cba3SSepherosa Ziehau 		 */
31526ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
31536ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
31546ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
31556ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
315689d55360SSepherosa Ziehau 	}
31578892ea20SAggelos Economopoulos 
3158b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
31598892ea20SAggelos Economopoulos 
3160b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
31616ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
31626ee6cba3SSepherosa Ziehau 	/*
31636ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
31646ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
31656ee6cba3SSepherosa Ziehau 	 */
31666ee6cba3SSepherosa Ziehau 
31676ee6cba3SSepherosa Ziehau 	/*
31686ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
31696ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
31706ee6cba3SSepherosa Ziehau 	 * of two. Luckily, FreeBSD's clusters are powers of two
31716ee6cba3SSepherosa Ziehau 	 */
31726ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
31738892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
31746ee6cba3SSepherosa Ziehau 
3175b9a8961fSSepherosa Ziehau 	/* XXX need to cut MXGEFW_PAD here? */
31768892ea20SAggelos Economopoulos 	cmd.data0 = MHLEN - MXGEFW_PAD;
31776ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
31786ee6cba3SSepherosa Ziehau 
3179b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
31808892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
31818892ea20SAggelos Economopoulos 
31828892ea20SAggelos Economopoulos 	if (err != 0) {
31836ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
31848892ea20SAggelos Economopoulos 		goto abort;
31858892ea20SAggelos Economopoulos 	}
31868892ea20SAggelos Economopoulos 
31878892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
31888892ea20SAggelos Economopoulos 	for (slice = 0;
31898892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
31908892ea20SAggelos Economopoulos 	     slice < sc->num_slices;
31918892ea20SAggelos Economopoulos #else
31928892ea20SAggelos Economopoulos 	     slice < 1;
31938892ea20SAggelos Economopoulos #endif
31948892ea20SAggelos Economopoulos 	     slice++) {
31958892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
31967cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
31977cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
31988892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
31998892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
32008892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
32018892ea20SAggelos Economopoulos 	}
32028892ea20SAggelos Economopoulos 
32038892ea20SAggelos Economopoulos 	if (err != 0) {
32047cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
32058892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
32068892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
32078892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
32086ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
32098892ea20SAggelos Economopoulos 		    &cmd);
32106ee6cba3SSepherosa Ziehau 
32118892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
32128892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
32138892ea20SAggelos Economopoulos 	} else {
32148892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
32158892ea20SAggelos Economopoulos 	}
32168892ea20SAggelos Economopoulos 
32178892ea20SAggelos Economopoulos 	if (err != 0) {
32186ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
32198892ea20SAggelos Economopoulos 		goto abort;
32208892ea20SAggelos Economopoulos 	}
32218892ea20SAggelos Economopoulos 
32228892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3223b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
32248892ea20SAggelos Economopoulos 		if (err != 0) {
32256ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
32268892ea20SAggelos Economopoulos 			goto abort;
32278892ea20SAggelos Economopoulos 		}
32288892ea20SAggelos Economopoulos 	}
32298892ea20SAggelos Economopoulos 
32308892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
32318892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
32328892ea20SAggelos Economopoulos 	if (err) {
32336ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
32348892ea20SAggelos Economopoulos 		goto abort;
32358892ea20SAggelos Economopoulos 	}
32366ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
32376ee6cba3SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
32388892ea20SAggelos Economopoulos 
32398892ea20SAggelos Economopoulos 	return 0;
32408892ea20SAggelos Economopoulos 
32418892ea20SAggelos Economopoulos abort:
32428892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
32438892ea20SAggelos Economopoulos 	return err;
32448892ea20SAggelos Economopoulos }
32458892ea20SAggelos Economopoulos 
32462c29ffc6SSepherosa Ziehau static void
324789d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
32488892ea20SAggelos Economopoulos {
32492c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
32508892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32518892ea20SAggelos Economopoulos 	int err, old_down_cnt;
32528892ea20SAggelos Economopoulos 
32532c29ffc6SSepherosa Ziehau 	ASSERT_SERIALIZED(ifp->if_serializer);
325489d55360SSepherosa Ziehau 
32552c29ffc6SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
32562c29ffc6SSepherosa Ziehau 	ifq_clr_oactive(&ifp->if_snd);
32572c29ffc6SSepherosa Ziehau 
325889d55360SSepherosa Ziehau 	if (!down) {
32598892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
32608892ea20SAggelos Economopoulos 		wmb();
32612c29ffc6SSepherosa Ziehau 
32628892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
32632c29ffc6SSepherosa Ziehau 		if (err)
32642c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
32652c29ffc6SSepherosa Ziehau 
32668892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
32672c29ffc6SSepherosa Ziehau 			/* Wait for down irq */
32682c29ffc6SSepherosa Ziehau 			lwkt_serialize_exit(ifp->if_serializer);
32698892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
32702c29ffc6SSepherosa Ziehau 			lwkt_serialize_enter(ifp->if_serializer);
32718892ea20SAggelos Economopoulos 		}
32722c29ffc6SSepherosa Ziehau 
32738892ea20SAggelos Economopoulos 		wmb();
32742c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
32752c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
327689d55360SSepherosa Ziehau 	}
32778892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
32788892ea20SAggelos Economopoulos }
32798892ea20SAggelos Economopoulos 
32808892ea20SAggelos Economopoulos static void
32818892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
32828892ea20SAggelos Economopoulos {
32838892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
32848892ea20SAggelos Economopoulos 	int reg;
328589d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
32868892ea20SAggelos Economopoulos 
32877cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
32888892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
32898892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
32908892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
32918892ea20SAggelos Economopoulos 
329289d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
32938892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
32948892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
32958892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
329689d55360SSepherosa Ziehau 			sc->pectl = pectl;
329789d55360SSepherosa Ziehau 		} else {
32987cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
329989d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
330089d55360SSepherosa Ziehau 		}
33018892ea20SAggelos Economopoulos 	}
33028892ea20SAggelos Economopoulos 
33037cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
33048892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
33058892ea20SAggelos Economopoulos }
33068892ea20SAggelos Economopoulos 
33078892ea20SAggelos Economopoulos static uint32_t
33088892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
33098892ea20SAggelos Economopoulos {
33108892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
33118892ea20SAggelos Economopoulos 	uint32_t vs;
33128892ea20SAggelos Economopoulos 
33138a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
33148892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
33158a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
33168892ea20SAggelos Economopoulos 		return (uint32_t)-1;
33178892ea20SAggelos Economopoulos 	}
33188a20b038SSepherosa Ziehau 	/* Enable read32 mode */
33198892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
33208a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
33218892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
33228a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
33238892ea20SAggelos Economopoulos }
33248892ea20SAggelos Economopoulos 
332589d55360SSepherosa Ziehau static void
332689d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
33278892ea20SAggelos Economopoulos {
33288892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
332989d55360SSepherosa Ziehau 	int err, running;
33308892ea20SAggelos Economopoulos 	uint32_t reboot;
33318892ea20SAggelos Economopoulos 	uint16_t cmd;
33328892ea20SAggelos Economopoulos 
33338892ea20SAggelos Economopoulos 	err = ENXIO;
33348892ea20SAggelos Economopoulos 
33358a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
33368892ea20SAggelos Economopoulos 
33378892ea20SAggelos Economopoulos 	/*
33388a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
33398892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
33408892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
33418892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
33428892ea20SAggelos Economopoulos 	 * again
33438892ea20SAggelos Economopoulos 	 */
33448892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33458892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
33468892ea20SAggelos Economopoulos 		/*
33478a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
33488892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
33498892ea20SAggelos Economopoulos 		 * back, then give up
33508892ea20SAggelos Economopoulos 		 */
33518892ea20SAggelos Economopoulos 		DELAY(1000*100);
33528892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
33538a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
33548a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
33558892ea20SAggelos Economopoulos 	}
33568892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
33578a20b038SSepherosa Ziehau 		/* Print the reboot status */
33588892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
33598a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
33608a20b038SSepherosa Ziehau 
336189d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
336289d55360SSepherosa Ziehau 		if (running) {
336389d55360SSepherosa Ziehau 			/*
33648a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
336589d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
336689d55360SSepherosa Ziehau 			 */
336789d55360SSepherosa Ziehau 
336889d55360SSepherosa Ziehau 			/* Mark the link as down */
336989d55360SSepherosa Ziehau 			if (sc->link_state) {
337089d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
337189d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
337289d55360SSepherosa Ziehau 			}
337389d55360SSepherosa Ziehau 			mxge_close(sc, 1);
337489d55360SSepherosa Ziehau 		}
33758a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
33768892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
33778892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
33788892ea20SAggelos Economopoulos 
33798a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
33808892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
33818892ea20SAggelos Economopoulos 
33828a20b038SSepherosa Ziehau 		/* Reload f/w */
338389d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
33848a20b038SSepherosa Ziehau 		if (err)
33858a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
33868a20b038SSepherosa Ziehau 		if (running && !err) {
338789d55360SSepherosa Ziehau 			err = mxge_open(sc);
338889d55360SSepherosa Ziehau 			if_devstart_sched(sc->ifp);
338989d55360SSepherosa Ziehau 		}
339089d55360SSepherosa Ziehau 		sc->watchdog_resets++;
339189d55360SSepherosa Ziehau 	} else {
33928a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
339389d55360SSepherosa Ziehau 		err = 0;
339489d55360SSepherosa Ziehau 	}
339589d55360SSepherosa Ziehau 	if (err) {
33968a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
339789d55360SSepherosa Ziehau 	} else {
339889d55360SSepherosa Ziehau 		if (sc->dying == 2)
339989d55360SSepherosa Ziehau 			sc->dying = 0;
340089d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
340189d55360SSepherosa Ziehau 	}
340289d55360SSepherosa Ziehau }
340389d55360SSepherosa Ziehau 
340489d55360SSepherosa Ziehau static void
340589d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
340689d55360SSepherosa Ziehau {
34078a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
34088a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
34098892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
34108a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
34118892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
34128a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
34138a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
34148892ea20SAggelos Economopoulos }
34158892ea20SAggelos Economopoulos 
34168892ea20SAggelos Economopoulos static int
34178892ea20SAggelos Economopoulos mxge_watchdog(mxge_softc_t *sc)
34188892ea20SAggelos Economopoulos {
34198892ea20SAggelos Economopoulos 	mxge_tx_ring_t *tx;
34208892ea20SAggelos Economopoulos 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
34218892ea20SAggelos Economopoulos 	int i, err = 0;
34228892ea20SAggelos Economopoulos 
34238892ea20SAggelos Economopoulos 	/* see if we have outstanding transmits, which
34248892ea20SAggelos Economopoulos 	   have been pending for more than mxge_ticks */
34258892ea20SAggelos Economopoulos 	for (i = 0;
34268892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
34278892ea20SAggelos Economopoulos 	     (i < sc->num_slices) && (err == 0);
34288892ea20SAggelos Economopoulos #else
34298892ea20SAggelos Economopoulos 	     (i < 1) && (err == 0);
34308892ea20SAggelos Economopoulos #endif
34318892ea20SAggelos Economopoulos 	     i++) {
34328892ea20SAggelos Economopoulos 		tx = &sc->ss[i].tx;
34338892ea20SAggelos Economopoulos 		if (tx->req != tx->done &&
34348892ea20SAggelos Economopoulos 		    tx->watchdog_req != tx->watchdog_done &&
34358892ea20SAggelos Economopoulos 		    tx->done == tx->watchdog_done) {
34368892ea20SAggelos Economopoulos 			/* check for pause blocking before resetting */
343789d55360SSepherosa Ziehau 			if (tx->watchdog_rx_pause == rx_pause) {
343889d55360SSepherosa Ziehau 				mxge_warn_stuck(sc, tx, i);
343989d55360SSepherosa Ziehau 				mxge_watchdog_reset(sc);
344089d55360SSepherosa Ziehau 				return (ENXIO);
344189d55360SSepherosa Ziehau 			}
34428892ea20SAggelos Economopoulos 			else
34438892ea20SAggelos Economopoulos 				device_printf(sc->dev, "Flow control blocking "
34448892ea20SAggelos Economopoulos 					      "xmits, check link partner\n");
34458892ea20SAggelos Economopoulos 		}
34468892ea20SAggelos Economopoulos 
34478892ea20SAggelos Economopoulos 		tx->watchdog_req = tx->req;
34488892ea20SAggelos Economopoulos 		tx->watchdog_done = tx->done;
34498892ea20SAggelos Economopoulos 		tx->watchdog_rx_pause = rx_pause;
34508892ea20SAggelos Economopoulos 	}
34518892ea20SAggelos Economopoulos 
34528892ea20SAggelos Economopoulos 	if (sc->need_media_probe)
34538892ea20SAggelos Economopoulos 		mxge_media_probe(sc);
34548892ea20SAggelos Economopoulos 	return (err);
34558892ea20SAggelos Economopoulos }
34568892ea20SAggelos Economopoulos 
345789d55360SSepherosa Ziehau static u_long
34588892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
34598892ea20SAggelos Economopoulos {
34608892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
346189d55360SSepherosa Ziehau 	u_long pkts = 0;
346289d55360SSepherosa Ziehau 	u_long ipackets = 0, old_ipackets;
346389d55360SSepherosa Ziehau 	u_long opackets = 0, old_opackets;
34648892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
34658892ea20SAggelos Economopoulos 	u_long obytes = 0;
34668892ea20SAggelos Economopoulos 	u_long omcasts = 0;
34678892ea20SAggelos Economopoulos 	u_long odrops = 0;
34688892ea20SAggelos Economopoulos #endif
34698892ea20SAggelos Economopoulos 	u_long oerrors = 0;
34708892ea20SAggelos Economopoulos 	int slice;
34718892ea20SAggelos Economopoulos 
34728892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
34738892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
34748892ea20SAggelos Economopoulos 		ipackets += ss->ipackets;
34758892ea20SAggelos Economopoulos 		opackets += ss->opackets;
34768892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
34778892ea20SAggelos Economopoulos 		obytes += ss->obytes;
34788892ea20SAggelos Economopoulos 		omcasts += ss->omcasts;
34798892ea20SAggelos Economopoulos 		odrops += ss->tx.br->br_drops;
34808892ea20SAggelos Economopoulos #endif
34818892ea20SAggelos Economopoulos 		oerrors += ss->oerrors;
34828892ea20SAggelos Economopoulos 	}
348389d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, old_ipackets);
348489d55360SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, old_opackets);
348589d55360SSepherosa Ziehau 
348689d55360SSepherosa Ziehau 	pkts = ipackets - old_ipackets;
348789d55360SSepherosa Ziehau 	pkts += opackets - old_opackets;
348889d55360SSepherosa Ziehau 
3489d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, ipackets, ipackets);
3490d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, opackets, opackets);
34918892ea20SAggelos Economopoulos #ifdef IFNET_BUF_RING
34928892ea20SAggelos Economopoulos 	sc->ifp->if_obytes = obytes;
34938892ea20SAggelos Economopoulos 	sc->ifp->if_omcasts = omcasts;
34948892ea20SAggelos Economopoulos 	sc->ifp->if_snd.ifq_drops = odrops;
34958892ea20SAggelos Economopoulos #endif
3496d40991efSSepherosa Ziehau 	IFNET_STAT_SET(sc->ifp, oerrors, oerrors);
349789d55360SSepherosa Ziehau 	return pkts;
34988892ea20SAggelos Economopoulos }
34998892ea20SAggelos Economopoulos 
35008892ea20SAggelos Economopoulos static void
35018892ea20SAggelos Economopoulos mxge_tick(void *arg)
35028892ea20SAggelos Economopoulos {
35038892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
350489d55360SSepherosa Ziehau 	u_long pkts = 0;
35058892ea20SAggelos Economopoulos 	int err = 0;
350689d55360SSepherosa Ziehau 	int running, ticks;
350789d55360SSepherosa Ziehau 	uint16_t cmd;
35088892ea20SAggelos Economopoulos 
35092e8181d0SAggelos Economopoulos 	lwkt_serialize_enter(sc->ifp->if_serializer);
351089d55360SSepherosa Ziehau 
351189d55360SSepherosa Ziehau 	ticks = mxge_ticks;
351289d55360SSepherosa Ziehau 	running = sc->ifp->if_flags & IFF_RUNNING;
351389d55360SSepherosa Ziehau 	if (running) {
35148892ea20SAggelos Economopoulos 		/* aggregate stats from different slices */
351589d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
35168892ea20SAggelos Economopoulos 		if (!sc->watchdog_countdown) {
35178892ea20SAggelos Economopoulos 			err = mxge_watchdog(sc);
35188892ea20SAggelos Economopoulos 			sc->watchdog_countdown = 4;
35198892ea20SAggelos Economopoulos 		}
35208892ea20SAggelos Economopoulos 		sc->watchdog_countdown--;
352189d55360SSepherosa Ziehau 	}
352289d55360SSepherosa Ziehau 	if (pkts == 0) {
352389d55360SSepherosa Ziehau 		/* ensure NIC did not suffer h/w fault while idle */
352489d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
352589d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
352689d55360SSepherosa Ziehau 			sc->dying = 2;
352789d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
352889d55360SSepherosa Ziehau 			err = ENXIO;
352989d55360SSepherosa Ziehau 		}
353089d55360SSepherosa Ziehau 		/* look less often if NIC is idle */
353189d55360SSepherosa Ziehau 		ticks *= 4;
353289d55360SSepherosa Ziehau 	}
353389d55360SSepherosa Ziehau 
35348892ea20SAggelos Economopoulos 	if (err == 0)
353589d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
353689d55360SSepherosa Ziehau 
35372e8181d0SAggelos Economopoulos 	lwkt_serialize_exit(sc->ifp->if_serializer);
35388892ea20SAggelos Economopoulos }
35398892ea20SAggelos Economopoulos 
35408892ea20SAggelos Economopoulos static int
35418892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
35428892ea20SAggelos Economopoulos {
35438892ea20SAggelos Economopoulos 	return EINVAL;
35448892ea20SAggelos Economopoulos }
35458892ea20SAggelos Economopoulos 
35468892ea20SAggelos Economopoulos static int
35478892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
35488892ea20SAggelos Economopoulos {
35498892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
35508892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
35518892ea20SAggelos Economopoulos 	int err = 0;
35528892ea20SAggelos Economopoulos 
3553b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3554b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
35558892ea20SAggelos Economopoulos 		return EINVAL;
3556b9a8961fSSepherosa Ziehau 
35578892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
35588892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
35592ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
356089d55360SSepherosa Ziehau 		mxge_close(sc, 0);
35618892ea20SAggelos Economopoulos 		err = mxge_open(sc);
35628892ea20SAggelos Economopoulos 		if (err != 0) {
35638892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
356489d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3565b9a8961fSSepherosa Ziehau 			mxge_open(sc);
35668892ea20SAggelos Economopoulos 		}
35678892ea20SAggelos Economopoulos 	}
35688892ea20SAggelos Economopoulos 	return err;
35698892ea20SAggelos Economopoulos }
35708892ea20SAggelos Economopoulos 
35718892ea20SAggelos Economopoulos static void
35728892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
35738892ea20SAggelos Economopoulos {
35748892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
35758892ea20SAggelos Economopoulos 
35768892ea20SAggelos Economopoulos 
35778892ea20SAggelos Economopoulos 	if (sc == NULL)
35788892ea20SAggelos Economopoulos 		return;
35798892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
358089d55360SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER | IFM_FDX;
35818892ea20SAggelos Economopoulos 	ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0;
358289d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
35838892ea20SAggelos Economopoulos }
35848892ea20SAggelos Economopoulos 
35858892ea20SAggelos Economopoulos static int
358689d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
358789d55360SSepherosa Ziehau     struct ucred *cr __unused)
35888892ea20SAggelos Economopoulos {
35898892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
35908892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
35918892ea20SAggelos Economopoulos 	int err, mask;
35928892ea20SAggelos Economopoulos 
359323811d63SAggelos Economopoulos 	ASSERT_SERIALIZED(ifp->if_serializer);
3594af85d4d5SSepherosa Ziehau 	err = 0;
3595af85d4d5SSepherosa Ziehau 
35968892ea20SAggelos Economopoulos 	switch (command) {
35978892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
35988892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
35998892ea20SAggelos Economopoulos 		break;
36008892ea20SAggelos Economopoulos 
36018892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3602af85d4d5SSepherosa Ziehau 		if (sc->dying)
36038892ea20SAggelos Economopoulos 			return EINVAL;
3604af85d4d5SSepherosa Ziehau 
36058892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
36062ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
36078892ea20SAggelos Economopoulos 				err = mxge_open(sc);
36088892ea20SAggelos Economopoulos 			} else {
3609af85d4d5SSepherosa Ziehau 				/*
3610af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3611af85d4d5SSepherosa Ziehau 				 * flag changes
3612af85d4d5SSepherosa Ziehau 				 */
36138892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
36148892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
36158892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
36168892ea20SAggelos Economopoulos 			}
36178892ea20SAggelos Economopoulos 		} else {
3618af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
361989d55360SSepherosa Ziehau 				mxge_close(sc, 0);
36208892ea20SAggelos Economopoulos 		}
36218892ea20SAggelos Economopoulos 		break;
36228892ea20SAggelos Economopoulos 
36238892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
36248892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
36258892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
36268892ea20SAggelos Economopoulos 		break;
36278892ea20SAggelos Economopoulos 
36288892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
36298892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
36308892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
363189d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
363289d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
363389d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
36348892ea20SAggelos Economopoulos 			else
363589d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
36368892ea20SAggelos Economopoulos 		}
363789d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
363889d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
363989d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
364089d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
364189d55360SSepherosa Ziehau 			else
364289d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
364389d55360SSepherosa Ziehau 		}
364489d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
364589d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
36468892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
36478892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
36488892ea20SAggelos Economopoulos 		break;
36498892ea20SAggelos Economopoulos 
36508892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
365189d55360SSepherosa Ziehau 		mxge_media_probe(sc);
36528892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
36538892ea20SAggelos Economopoulos 		    &sc->media, command);
36548892ea20SAggelos Economopoulos 		break;
36558892ea20SAggelos Economopoulos 
36568892ea20SAggelos Economopoulos 	default:
365789d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
365889d55360SSepherosa Ziehau 		break;
36598892ea20SAggelos Economopoulos 	}
36608892ea20SAggelos Economopoulos 	return err;
36618892ea20SAggelos Economopoulos }
36628892ea20SAggelos Economopoulos 
36638892ea20SAggelos Economopoulos static void
36648892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
36658892ea20SAggelos Economopoulos {
36667cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
36677cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
36687cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
36698892ea20SAggelos Economopoulos 
36707cc92483SSepherosa Ziehau 	/* XXX */
36718892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
36728892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
36737cc92483SSepherosa Ziehau 
36748892ea20SAggelos Economopoulos 	sc->pause = mxge_flow_control;
36758892ea20SAggelos Economopoulos 
367689d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
36777cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
36787cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
36797cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
36807cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
368189d55360SSepherosa Ziehau }
36828892ea20SAggelos Economopoulos 
36838892ea20SAggelos Economopoulos static void
36848892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
36858892ea20SAggelos Economopoulos {
36868892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
36878892ea20SAggelos Economopoulos 	int i;
36888892ea20SAggelos Economopoulos 
36898892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
36908892ea20SAggelos Economopoulos 		return;
36918892ea20SAggelos Economopoulos 
36928892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
36938892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
36948892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
36958892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
36968892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
36978892ea20SAggelos Economopoulos 		}
36988892ea20SAggelos Economopoulos 		if (ss->rx_done.entry != NULL) {
36998892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->rx_done.dma);
37008892ea20SAggelos Economopoulos 			ss->rx_done.entry = NULL;
37018892ea20SAggelos Economopoulos 		}
37028892ea20SAggelos Economopoulos 	}
37036c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
37048892ea20SAggelos Economopoulos 	sc->ss = NULL;
37058892ea20SAggelos Economopoulos }
37068892ea20SAggelos Economopoulos 
37078892ea20SAggelos Economopoulos static int
37088892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
37098892ea20SAggelos Economopoulos {
37108892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
37118892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
37128892ea20SAggelos Economopoulos 	size_t bytes;
37138892ea20SAggelos Economopoulos 	int err, i, max_intr_slots;
37148892ea20SAggelos Economopoulos 
37158892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
37168892ea20SAggelos Economopoulos 	if (err != 0) {
37178892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
37188892ea20SAggelos Economopoulos 		return err;
37198892ea20SAggelos Economopoulos 	}
37208892ea20SAggelos Economopoulos 	sc->rx_ring_size = cmd.data0;
37218892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t));
37228892ea20SAggelos Economopoulos 
37238892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
37247cc92483SSepherosa Ziehau 	sc->ss = kmalloc(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
37257cc92483SSepherosa Ziehau 
37268892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
37278892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
37288892ea20SAggelos Economopoulos 
37298892ea20SAggelos Economopoulos 		ss->sc = sc;
37308892ea20SAggelos Economopoulos 
37317cc92483SSepherosa Ziehau 		/*
37327cc92483SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queues
37337cc92483SSepherosa Ziehau 		 */
37348892ea20SAggelos Economopoulos 		bytes = max_intr_slots * sizeof(*ss->rx_done.entry);
37358892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096);
3736798c3369SSepherosa Ziehau 		if (err != 0) {
3737798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3738798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3739798c3369SSepherosa Ziehau 			return err;
3740798c3369SSepherosa Ziehau 		}
37417cc92483SSepherosa Ziehau 		ss->rx_done.entry = ss->rx_done.dma.dmem_addr;
37428892ea20SAggelos Economopoulos 
37438892ea20SAggelos Economopoulos 		/*
37447cc92483SSepherosa Ziehau 		 * Allocate the per-slice firmware stats; stats
37458892ea20SAggelos Economopoulos 		 * (including tx) are used used only on the first
37468892ea20SAggelos Economopoulos 		 * slice for now
37478892ea20SAggelos Economopoulos 		 */
37488892ea20SAggelos Economopoulos #ifndef IFNET_BUF_RING
37498892ea20SAggelos Economopoulos 		if (i > 0)
37508892ea20SAggelos Economopoulos 			continue;
37518892ea20SAggelos Economopoulos #endif
37528892ea20SAggelos Economopoulos 
37538892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
37548892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
37558892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3756798c3369SSepherosa Ziehau 		if (err != 0) {
3757798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3758798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3759798c3369SSepherosa Ziehau 			return err;
3760798c3369SSepherosa Ziehau 		}
37617cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
37628892ea20SAggelos Economopoulos 	}
37637cc92483SSepherosa Ziehau 	return 0;
37648892ea20SAggelos Economopoulos }
37658892ea20SAggelos Economopoulos 
37668892ea20SAggelos Economopoulos static void
37678892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
37688892ea20SAggelos Economopoulos {
37698892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3770c7431c78SSepherosa Ziehau 	const char *old_fw;
37718892ea20SAggelos Economopoulos 	int msix_cnt, status, max_intr_slots;
37728892ea20SAggelos Economopoulos 
37738892ea20SAggelos Economopoulos 	sc->num_slices = 1;
37747cc92483SSepherosa Ziehau 
37758892ea20SAggelos Economopoulos 	/*
37767cc92483SSepherosa Ziehau 	 * XXX
37777cc92483SSepherosa Ziehau 	 *
37787cc92483SSepherosa Ziehau 	 * Don't enable multiple slices if they are not enabled,
37798892ea20SAggelos Economopoulos 	 * or if this is not an SMP system
37808892ea20SAggelos Economopoulos 	 */
3781b9596feeSAggelos Economopoulos 	if (mxge_max_slices == 0 || mxge_max_slices == 1 || ncpus < 2)
37828892ea20SAggelos Economopoulos 		return;
37838892ea20SAggelos Economopoulos 
37848892ea20SAggelos Economopoulos 	/* see how many MSI-X interrupts are available */
37858892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
37868892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
37878892ea20SAggelos Economopoulos 		return;
37888892ea20SAggelos Economopoulos 
37898892ea20SAggelos Economopoulos 	/* now load the slice aware firmware see what it supports */
37908892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
37918892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
37928892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
37938892ea20SAggelos Economopoulos 	else
37948892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
37958892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
37968892ea20SAggelos Economopoulos 	if (status != 0) {
37978892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
37988892ea20SAggelos Economopoulos 		return;
37998892ea20SAggelos Economopoulos 	}
38008892ea20SAggelos Economopoulos 
38018892ea20SAggelos Economopoulos 	/* try to send a reset command to the card to see if it
38028892ea20SAggelos Economopoulos 	   is alive */
38038892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
38048892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
38058892ea20SAggelos Economopoulos 	if (status != 0) {
38068892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
38078892ea20SAggelos Economopoulos 		goto abort_with_fw;
38088892ea20SAggelos Economopoulos 	}
38098892ea20SAggelos Economopoulos 
38108892ea20SAggelos Economopoulos 	/* get rx ring size */
38118892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
38128892ea20SAggelos Economopoulos 	if (status != 0) {
38138892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
38148892ea20SAggelos Economopoulos 		goto abort_with_fw;
38158892ea20SAggelos Economopoulos 	}
38168892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t));
38178892ea20SAggelos Economopoulos 
38188892ea20SAggelos Economopoulos 	/* tell it the size of the interrupt queues */
38198892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof (struct mcp_slot);
38208892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
38218892ea20SAggelos Economopoulos 	if (status != 0) {
38228892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
38238892ea20SAggelos Economopoulos 		goto abort_with_fw;
38248892ea20SAggelos Economopoulos 	}
38258892ea20SAggelos Economopoulos 
38268892ea20SAggelos Economopoulos 	/* ask the maximum number of slices it supports */
38278892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
38288892ea20SAggelos Economopoulos 	if (status != 0) {
38298892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38308892ea20SAggelos Economopoulos 			      "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
38318892ea20SAggelos Economopoulos 		goto abort_with_fw;
38328892ea20SAggelos Economopoulos 	}
38338892ea20SAggelos Economopoulos 	sc->num_slices = cmd.data0;
38348892ea20SAggelos Economopoulos 	if (sc->num_slices > msix_cnt)
38358892ea20SAggelos Economopoulos 		sc->num_slices = msix_cnt;
38368892ea20SAggelos Economopoulos 
38378892ea20SAggelos Economopoulos 	if (mxge_max_slices == -1) {
38388892ea20SAggelos Economopoulos 		/* cap to number of CPUs in system */
3839ae7ed840SAggelos Economopoulos 		if (sc->num_slices > ncpus)
3840ae7ed840SAggelos Economopoulos 			sc->num_slices = ncpus;
38418892ea20SAggelos Economopoulos 	} else {
38428892ea20SAggelos Economopoulos 		if (sc->num_slices > mxge_max_slices)
38438892ea20SAggelos Economopoulos 			sc->num_slices = mxge_max_slices;
38448892ea20SAggelos Economopoulos 	}
38458892ea20SAggelos Economopoulos 	/* make sure it is a power of two */
38468892ea20SAggelos Economopoulos 	while (sc->num_slices & (sc->num_slices - 1))
38478892ea20SAggelos Economopoulos 		sc->num_slices--;
38488892ea20SAggelos Economopoulos 
38497cc92483SSepherosa Ziehau 	if (bootverbose)
38508892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d slices\n",
38518892ea20SAggelos Economopoulos 			      sc->num_slices);
38528892ea20SAggelos Economopoulos 
38538892ea20SAggelos Economopoulos 	return;
38548892ea20SAggelos Economopoulos 
38558892ea20SAggelos Economopoulos abort_with_fw:
38568892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
38578892ea20SAggelos Economopoulos 	(void) mxge_load_firmware(sc, 0);
38588892ea20SAggelos Economopoulos }
38598892ea20SAggelos Economopoulos 
3860a26af990SSepherosa Ziehau #if 0
38618892ea20SAggelos Economopoulos static int
38628892ea20SAggelos Economopoulos mxge_add_msix_irqs(mxge_softc_t *sc)
38638892ea20SAggelos Economopoulos {
38648892ea20SAggelos Economopoulos 	size_t bytes;
38658892ea20SAggelos Economopoulos 	int count, err, i, rid;
38668892ea20SAggelos Economopoulos 
38678892ea20SAggelos Economopoulos 	rid = PCIR_BAR(2);
38688892ea20SAggelos Economopoulos 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
38698892ea20SAggelos Economopoulos 						    &rid, RF_ACTIVE);
38708892ea20SAggelos Economopoulos 
38718892ea20SAggelos Economopoulos 	if (sc->msix_table_res == NULL) {
38728892ea20SAggelos Economopoulos 		device_printf(sc->dev, "couldn't alloc MSIX table res\n");
38738892ea20SAggelos Economopoulos 		return ENXIO;
38748892ea20SAggelos Economopoulos 	}
38758892ea20SAggelos Economopoulos 
38768892ea20SAggelos Economopoulos 	count = sc->num_slices;
38778892ea20SAggelos Economopoulos 	err = pci_alloc_msix(sc->dev, &count);
38788892ea20SAggelos Economopoulos 	if (err != 0) {
38798892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d"
38808892ea20SAggelos Economopoulos 			      "err = %d \n", sc->num_slices, err);
38818892ea20SAggelos Economopoulos 		goto abort_with_msix_table;
38828892ea20SAggelos Economopoulos 	}
38838892ea20SAggelos Economopoulos 	if (count < sc->num_slices) {
38848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n",
38858892ea20SAggelos Economopoulos 			      count, sc->num_slices);
38868892ea20SAggelos Economopoulos 		device_printf(sc->dev,
38878892ea20SAggelos Economopoulos 			      "Try setting hw.mxge.max_slices to %d\n",
38888892ea20SAggelos Economopoulos 			      count);
38898892ea20SAggelos Economopoulos 		err = ENOSPC;
38908892ea20SAggelos Economopoulos 		goto abort_with_msix;
38918892ea20SAggelos Economopoulos 	}
38928892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_irq_res) * sc->num_slices;
3893d777b84fSAggelos Economopoulos 	sc->msix_irq_res = kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
38948892ea20SAggelos Economopoulos 	if (sc->msix_irq_res == NULL) {
38958892ea20SAggelos Economopoulos 		err = ENOMEM;
38968892ea20SAggelos Economopoulos 		goto abort_with_msix;
38978892ea20SAggelos Economopoulos 	}
38988892ea20SAggelos Economopoulos 
38998892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39008892ea20SAggelos Economopoulos 		rid = i + 1;
39018892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev,
39028892ea20SAggelos Economopoulos 							  SYS_RES_IRQ,
39038892ea20SAggelos Economopoulos 							  &rid, RF_ACTIVE);
39048892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] == NULL) {
39058892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't allocate IRQ res"
39068892ea20SAggelos Economopoulos 				      " for message %d\n", i);
39078892ea20SAggelos Economopoulos 			err = ENXIO;
39088892ea20SAggelos Economopoulos 			goto abort_with_res;
39098892ea20SAggelos Economopoulos 		}
39108892ea20SAggelos Economopoulos 	}
39118892ea20SAggelos Economopoulos 
39128892ea20SAggelos Economopoulos 	bytes = sizeof (*sc->msix_ih) * sc->num_slices;
3913d777b84fSAggelos Economopoulos 	sc->msix_ih =  kmalloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO);
39148892ea20SAggelos Economopoulos 
39158892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39168892ea20SAggelos Economopoulos 		err = bus_setup_intr(sc->dev, sc->msix_irq_res[i],
39177d8771d4SAggelos Economopoulos 				     INTR_MPSAFE,
39187d8771d4SAggelos Economopoulos 				     mxge_intr, &sc->ss[i], &sc->msix_ih[i],
39192e8181d0SAggelos Economopoulos 				     sc->ifp->if_serializer);
39208892ea20SAggelos Economopoulos 		if (err != 0) {
39218892ea20SAggelos Economopoulos 			device_printf(sc->dev, "couldn't setup intr for "
39228892ea20SAggelos Economopoulos 				      "message %d\n", i);
39238892ea20SAggelos Economopoulos 			goto abort_with_intr;
39248892ea20SAggelos Economopoulos 		}
39258892ea20SAggelos Economopoulos 	}
39268892ea20SAggelos Economopoulos 
39277cc92483SSepherosa Ziehau 	if (bootverbose) {
39288892ea20SAggelos Economopoulos 		device_printf(sc->dev, "using %d msix IRQs:",
39298892ea20SAggelos Economopoulos 			      sc->num_slices);
39308892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
39316c348da6SAggelos Economopoulos 			kprintf(" %ld",  rman_get_start(sc->msix_irq_res[i]));
39326c348da6SAggelos Economopoulos 		kprintf("\n");
39338892ea20SAggelos Economopoulos 	}
39348892ea20SAggelos Economopoulos 	return (0);
39358892ea20SAggelos Economopoulos 
39368892ea20SAggelos Economopoulos abort_with_intr:
39378892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39388892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39398892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39408892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39418892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39428892ea20SAggelos Economopoulos 		}
39438892ea20SAggelos Economopoulos 	}
3944d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
39458892ea20SAggelos Economopoulos 
39468892ea20SAggelos Economopoulos 
39478892ea20SAggelos Economopoulos abort_with_res:
39488892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39498892ea20SAggelos Economopoulos 		rid = i + 1;
39508892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
39518892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
39528892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
39538892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
39548892ea20SAggelos Economopoulos 	}
3955d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
39568892ea20SAggelos Economopoulos 
39578892ea20SAggelos Economopoulos 
39588892ea20SAggelos Economopoulos abort_with_msix:
39598892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
39608892ea20SAggelos Economopoulos 
39618892ea20SAggelos Economopoulos abort_with_msix_table:
39628892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
39638892ea20SAggelos Economopoulos 			     sc->msix_table_res);
39648892ea20SAggelos Economopoulos 
39658892ea20SAggelos Economopoulos 	return err;
39668892ea20SAggelos Economopoulos }
3967a26af990SSepherosa Ziehau #endif
39688892ea20SAggelos Economopoulos 
39698892ea20SAggelos Economopoulos static int
39708892ea20SAggelos Economopoulos mxge_add_single_irq(mxge_softc_t *sc)
39718892ea20SAggelos Economopoulos {
397289d55360SSepherosa Ziehau 	u_int irq_flags;
397351c70c94SSascha Wildner 
39747cc92483SSepherosa Ziehau 	sc->irq_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
39757cc92483SSepherosa Ziehau 	    &sc->irq_rid, &irq_flags);
397689d55360SSepherosa Ziehau 
397789d55360SSepherosa Ziehau 	sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
397889d55360SSepherosa Ziehau 	    &sc->irq_rid, irq_flags);
39798892ea20SAggelos Economopoulos 	if (sc->irq_res == NULL) {
39808892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not alloc interrupt\n");
39818892ea20SAggelos Economopoulos 		return ENXIO;
39828892ea20SAggelos Economopoulos 	}
398389d55360SSepherosa Ziehau 
3984798c3369SSepherosa Ziehau 	return bus_setup_intr(sc->dev, sc->irq_res, INTR_MPSAFE,
39857cc92483SSepherosa Ziehau 	    mxge_intr, &sc->ss[0], &sc->ih, sc->ifp->if_serializer);
39868892ea20SAggelos Economopoulos }
39878892ea20SAggelos Economopoulos 
3988a26af990SSepherosa Ziehau #if 0
39898892ea20SAggelos Economopoulos static void
39908892ea20SAggelos Economopoulos mxge_rem_msix_irqs(mxge_softc_t *sc)
39918892ea20SAggelos Economopoulos {
39928892ea20SAggelos Economopoulos 	int i, rid;
39938892ea20SAggelos Economopoulos 
39948892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39958892ea20SAggelos Economopoulos 		if (sc->msix_ih[i] != NULL) {
39968892ea20SAggelos Economopoulos 			bus_teardown_intr(sc->dev, sc->msix_irq_res[i],
39978892ea20SAggelos Economopoulos 					  sc->msix_ih[i]);
39988892ea20SAggelos Economopoulos 			sc->msix_ih[i] = NULL;
39998892ea20SAggelos Economopoulos 		}
40008892ea20SAggelos Economopoulos 	}
4001d777b84fSAggelos Economopoulos 	kfree(sc->msix_ih, M_DEVBUF);
40028892ea20SAggelos Economopoulos 
40038892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
40048892ea20SAggelos Economopoulos 		rid = i + 1;
40058892ea20SAggelos Economopoulos 		if (sc->msix_irq_res[i] != NULL)
40068892ea20SAggelos Economopoulos 			bus_release_resource(sc->dev, SYS_RES_IRQ, rid,
40078892ea20SAggelos Economopoulos 					     sc->msix_irq_res[i]);
40088892ea20SAggelos Economopoulos 		sc->msix_irq_res[i] = NULL;
40098892ea20SAggelos Economopoulos 	}
4010d777b84fSAggelos Economopoulos 	kfree(sc->msix_irq_res, M_DEVBUF);
40118892ea20SAggelos Economopoulos 
40128892ea20SAggelos Economopoulos 	bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2),
40138892ea20SAggelos Economopoulos 			     sc->msix_table_res);
40148892ea20SAggelos Economopoulos 
40158892ea20SAggelos Economopoulos 	pci_release_msi(sc->dev);
40168892ea20SAggelos Economopoulos 	return;
40178892ea20SAggelos Economopoulos }
4018a26af990SSepherosa Ziehau #endif
40198892ea20SAggelos Economopoulos 
40208892ea20SAggelos Economopoulos static int
40218892ea20SAggelos Economopoulos mxge_add_irq(mxge_softc_t *sc)
40228892ea20SAggelos Economopoulos {
4023a26af990SSepherosa Ziehau #if 0
40248892ea20SAggelos Economopoulos 	int err;
40258892ea20SAggelos Economopoulos 
40268892ea20SAggelos Economopoulos 	if (sc->num_slices > 1)
40278892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40288892ea20SAggelos Economopoulos 	else
40298892ea20SAggelos Economopoulos 		err = mxge_add_single_irq(sc);
40308892ea20SAggelos Economopoulos 
40318892ea20SAggelos Economopoulos 	if (0 && err == 0 && sc->num_slices > 1) {
40328892ea20SAggelos Economopoulos 		mxge_rem_msix_irqs(sc);
40338892ea20SAggelos Economopoulos 		err = mxge_add_msix_irqs(sc);
40348892ea20SAggelos Economopoulos 	}
40358892ea20SAggelos Economopoulos 	return err;
4036a26af990SSepherosa Ziehau #else
4037a26af990SSepherosa Ziehau 	return mxge_add_single_irq(sc);
4038a26af990SSepherosa Ziehau #endif
40398892ea20SAggelos Economopoulos }
40408892ea20SAggelos Economopoulos 
40418892ea20SAggelos Economopoulos static int
40428892ea20SAggelos Economopoulos mxge_attach(device_t dev)
40438892ea20SAggelos Economopoulos {
40448892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4045137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
40468892ea20SAggelos Economopoulos 	int err, rid;
40478892ea20SAggelos Economopoulos 
4048f0115d64SAggelos Economopoulos 	/*
40497cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4050f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4051f0115d64SAggelos Economopoulos 	 */
4052f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
40538892ea20SAggelos Economopoulos 	sc->dev = dev;
40547cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
4055798c3369SSepherosa Ziehau 	ifmedia_init(&sc->media, 0, mxge_media_change, mxge_media_status);
40567cc92483SSepherosa Ziehau 
40578892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
40588892ea20SAggelos Economopoulos 
40598892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
40608892ea20SAggelos Economopoulos 				 1,			/* alignment */
40618892ea20SAggelos Economopoulos 				 0,			/* boundary */
40628892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
40638892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
40648892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
40657cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
40667cc92483SSepherosa Ziehau 				 0, 			/* num segs */
40677cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
40688892ea20SAggelos Economopoulos 				 0,			/* flags */
40698892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
40708892ea20SAggelos Economopoulos 	if (err != 0) {
4071798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4072798c3369SSepherosa Ziehau 		goto failed;
40738892ea20SAggelos Economopoulos 	}
40748892ea20SAggelos Economopoulos 
4075e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
40768892ea20SAggelos Economopoulos 
40778892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
40788892ea20SAggelos Economopoulos 
40797cc92483SSepherosa Ziehau 	/*
40807cc92483SSepherosa Ziehau 	 * Map the board into the kernel
40817cc92483SSepherosa Ziehau 	 */
40828892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
40837cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
40847cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
40858892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
40868892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
40878892ea20SAggelos Economopoulos 		err = ENXIO;
4088798c3369SSepherosa Ziehau 		goto failed;
40898892ea20SAggelos Economopoulos 	}
40907cc92483SSepherosa Ziehau 
40918892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
40928892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
40938892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
40948892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
40958892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
40968892ea20SAggelos Economopoulos 		err = ENXIO;
4097798c3369SSepherosa Ziehau 		goto failed;
40988892ea20SAggelos Economopoulos 	}
40998892ea20SAggelos Economopoulos 
41007cc92483SSepherosa Ziehau 	/*
41017cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
41027cc92483SSepherosa Ziehau 	 * lanai SRAM
41037cc92483SSepherosa Ziehau 	 */
41048892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
41058892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
41068892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
41078892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
41087cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
41098892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4110798c3369SSepherosa Ziehau 	if (err != 0) {
4111798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4112798c3369SSepherosa Ziehau 		goto failed;
4113798c3369SSepherosa Ziehau 	}
41148892ea20SAggelos Economopoulos 
41157cc92483SSepherosa Ziehau 	/*
41167cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
41177cc92483SSepherosa Ziehau 	 */
41188892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
41198892ea20SAggelos Economopoulos 
41207cc92483SSepherosa Ziehau 	/*
41217cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
41227cc92483SSepherosa Ziehau 	 */
41237cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4124798c3369SSepherosa Ziehau 	if (err != 0) {
4125798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4126798c3369SSepherosa Ziehau 		goto failed;
4127798c3369SSepherosa Ziehau 	}
41287cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
41297cc92483SSepherosa Ziehau 
41308892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4131798c3369SSepherosa Ziehau 	if (err != 0) {
4132798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4133798c3369SSepherosa Ziehau 		goto failed;
4134798c3369SSepherosa Ziehau 	}
41358892ea20SAggelos Economopoulos 
41368892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4137798c3369SSepherosa Ziehau 	if (err != 0) {
4138798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4139798c3369SSepherosa Ziehau 		goto failed;
4140798c3369SSepherosa Ziehau 	}
41418892ea20SAggelos Economopoulos 
41427cc92483SSepherosa Ziehau 	/* Select & load the firmware */
41438892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4144798c3369SSepherosa Ziehau 	if (err != 0) {
4145798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4146798c3369SSepherosa Ziehau 		goto failed;
4147798c3369SSepherosa Ziehau 	}
41488892ea20SAggelos Economopoulos 
41498892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
41508892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4151798c3369SSepherosa Ziehau 	if (err != 0) {
4152798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4153798c3369SSepherosa Ziehau 		goto failed;
4154798c3369SSepherosa Ziehau 	}
41558892ea20SAggelos Economopoulos 
41568892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4157798c3369SSepherosa Ziehau 	if (err != 0) {
4158798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4159798c3369SSepherosa Ziehau 		goto failed;
4160798c3369SSepherosa Ziehau 	}
41618892ea20SAggelos Economopoulos 
41628892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
41638892ea20SAggelos Economopoulos 	if (err != 0) {
4164798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4165798c3369SSepherosa Ziehau 		goto failed;
41668892ea20SAggelos Economopoulos 	}
41678892ea20SAggelos Economopoulos 
41688892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
416989d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
41708892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
417189d55360SSepherosa Ziehau 
417289d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
417389d55360SSepherosa Ziehau #if 0
417489d55360SSepherosa Ziehau 	/* Well, its software, sigh */
417589d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
417689d55360SSepherosa Ziehau #endif
41778892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
417889d55360SSepherosa Ziehau 
41798892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
41808892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
418189d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
41828892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
41838892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
418489d55360SSepherosa Ziehau 	/* XXX watchdog */
418589d55360SSepherosa Ziehau 
4186820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4187820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4188820e213fSSepherosa Ziehau 
41898892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
419089d55360SSepherosa Ziehau 	mxge_media_init(sc);
41918892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
419289d55360SSepherosa Ziehau 
4193cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
419489d55360SSepherosa Ziehau 
4195b9a8961fSSepherosa Ziehau 	/*
4196b9a8961fSSepherosa Ziehau 	 * XXX
4197b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4198b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4199b9a8961fSSepherosa Ziehau 	 */
4200b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4201b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
420289d55360SSepherosa Ziehau 	sc->dying = 0;
420389d55360SSepherosa Ziehau 
4204369c353eSAggelos Economopoulos 	/* must come after ether_ifattach() */
4205369c353eSAggelos Economopoulos 	err = mxge_add_irq(sc);
4206369c353eSAggelos Economopoulos 	if (err != 0) {
4207798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4208798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4209798c3369SSepherosa Ziehau 		goto failed;
4210369c353eSAggelos Economopoulos 	}
421189d55360SSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->irq_res));
42128892ea20SAggelos Economopoulos 
42138892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
421489d55360SSepherosa Ziehau 
421589d55360SSepherosa Ziehau 	callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
42168892ea20SAggelos Economopoulos 	return 0;
42178892ea20SAggelos Economopoulos 
4218798c3369SSepherosa Ziehau failed:
4219798c3369SSepherosa Ziehau 	mxge_detach(dev);
42208892ea20SAggelos Economopoulos 	return err;
42218892ea20SAggelos Economopoulos }
42228892ea20SAggelos Economopoulos 
42238892ea20SAggelos Economopoulos static int
42248892ea20SAggelos Economopoulos mxge_detach(device_t dev)
42258892ea20SAggelos Economopoulos {
42268892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
42278892ea20SAggelos Economopoulos 
4228798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4229798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
4230798c3369SSepherosa Ziehau 
4231798c3369SSepherosa Ziehau 		lwkt_serialize_enter(ifp->if_serializer);
4232798c3369SSepherosa Ziehau 
42338892ea20SAggelos Economopoulos 		sc->dying = 1;
4234798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
423589d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4236e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4237798c3369SSepherosa Ziehau 
4238798c3369SSepherosa Ziehau 		bus_teardown_intr(sc->dev, sc->irq_res, sc->ih);
4239798c3369SSepherosa Ziehau 
4240798c3369SSepherosa Ziehau 		lwkt_serialize_exit(ifp->if_serializer);
4241e3dc37faSAggelos Economopoulos 
424289d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
424389d55360SSepherosa Ziehau 
4244798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4245798c3369SSepherosa Ziehau 	}
42468892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4247798c3369SSepherosa Ziehau 
4248798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4249798c3369SSepherosa Ziehau 	    sc->sram != NULL)
42508892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4251798c3369SSepherosa Ziehau 
42528892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
42538892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4254798c3369SSepherosa Ziehau 
4255798c3369SSepherosa Ziehau 	/* MUST after sysctls and rings are freed */
42568892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4257798c3369SSepherosa Ziehau 
4258798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
42598892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4260798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
42618892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4262798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
42638892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4264798c3369SSepherosa Ziehau 
4265798c3369SSepherosa Ziehau 	if (sc->irq_res != NULL) {
4266798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
4267798c3369SSepherosa Ziehau 		    sc->irq_res);
4268798c3369SSepherosa Ziehau 	}
4269798c3369SSepherosa Ziehau 	if (sc->irq_type == PCI_INTR_TYPE_MSI)
4270798c3369SSepherosa Ziehau 		pci_release_msi(dev);
4271798c3369SSepherosa Ziehau 
4272798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4273798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4274798c3369SSepherosa Ziehau 		    sc->mem_res);
4275798c3369SSepherosa Ziehau 	}
4276798c3369SSepherosa Ziehau 
4277798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
42788892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4279798c3369SSepherosa Ziehau 
42808892ea20SAggelos Economopoulos 	return 0;
42818892ea20SAggelos Economopoulos }
42828892ea20SAggelos Economopoulos 
42838892ea20SAggelos Economopoulos static int
42848892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
42858892ea20SAggelos Economopoulos {
42868892ea20SAggelos Economopoulos 	return 0;
42878892ea20SAggelos Economopoulos }
4288