xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision 57e09377)
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 
322276707eSSepherosa Ziehau #include "opt_ifpoll.h"
3389d55360SSepherosa Ziehau #include "opt_inet.h"
3489d55360SSepherosa Ziehau 
358892ea20SAggelos Economopoulos #include <sys/param.h>
368892ea20SAggelos Economopoulos #include <sys/systm.h>
378892ea20SAggelos Economopoulos #include <sys/linker.h>
388892ea20SAggelos Economopoulos #include <sys/firmware.h>
398892ea20SAggelos Economopoulos #include <sys/endian.h>
4005e71c89SAggelos Economopoulos #include <sys/in_cksum.h>
418892ea20SAggelos Economopoulos #include <sys/sockio.h>
428892ea20SAggelos Economopoulos #include <sys/mbuf.h>
438892ea20SAggelos Economopoulos #include <sys/malloc.h>
448892ea20SAggelos Economopoulos #include <sys/kernel.h>
458892ea20SAggelos Economopoulos #include <sys/module.h>
462e8181d0SAggelos Economopoulos #include <sys/serialize.h>
478892ea20SAggelos Economopoulos #include <sys/socket.h>
488892ea20SAggelos Economopoulos #include <sys/sysctl.h>
498892ea20SAggelos Economopoulos 
508892ea20SAggelos Economopoulos #include <net/if.h>
518892ea20SAggelos Economopoulos #include <net/if_arp.h>
52f2f758dfSAggelos Economopoulos #include <net/ifq_var.h>
538892ea20SAggelos Economopoulos #include <net/ethernet.h>
548892ea20SAggelos Economopoulos #include <net/if_dl.h>
558892ea20SAggelos Economopoulos #include <net/if_media.h>
562276707eSSepherosa Ziehau #include <net/if_poll.h>
578892ea20SAggelos Economopoulos 
588892ea20SAggelos Economopoulos #include <net/bpf.h>
598892ea20SAggelos Economopoulos 
608892ea20SAggelos Economopoulos #include <net/if_types.h>
61b3535a6fSAggelos Economopoulos #include <net/vlan/if_vlan_var.h>
628892ea20SAggelos Economopoulos #include <net/zlib.h>
631cd61a7cSSepherosa Ziehau #include <net/toeplitz.h>
648892ea20SAggelos Economopoulos 
658892ea20SAggelos Economopoulos #include <netinet/in_systm.h>
668892ea20SAggelos Economopoulos #include <netinet/in.h>
678892ea20SAggelos Economopoulos #include <netinet/ip.h>
688892ea20SAggelos Economopoulos #include <netinet/tcp.h>
698892ea20SAggelos Economopoulos 
708892ea20SAggelos Economopoulos #include <sys/bus.h>
718892ea20SAggelos Economopoulos #include <sys/rman.h>
728892ea20SAggelos Economopoulos 
73b3535a6fSAggelos Economopoulos #include <bus/pci/pcireg.h>
74b3535a6fSAggelos Economopoulos #include <bus/pci/pcivar.h>
75b3535a6fSAggelos Economopoulos #include <bus/pci/pci_private.h> /* XXX for pci_cfg_restore */
768892ea20SAggelos Economopoulos 
778892ea20SAggelos Economopoulos #include <vm/vm.h>		/* for pmap_mapdev() */
788892ea20SAggelos Economopoulos #include <vm/pmap.h>
798892ea20SAggelos Economopoulos 
8089d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
818892ea20SAggelos Economopoulos #include <machine/specialreg.h>
828892ea20SAggelos Economopoulos #endif
838892ea20SAggelos Economopoulos 
84b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mxge_mcp.h>
85b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mcp_gen_header.h>
86b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/if_mxge_var.h>
878892ea20SAggelos Economopoulos 
8800f2de12SSepherosa Ziehau #define MXGE_IFM	(IFM_ETHER | IFM_FDX | IFM_ETH_FORCEPAUSE)
8900f2de12SSepherosa Ziehau 
9060f8c66dSSepherosa Ziehau #define MXGE_RX_SMALL_BUFLEN		(MHLEN - MXGEFW_PAD)
911cd61a7cSSepherosa Ziehau #define MXGE_HWRSS_KEYLEN		16
9260f8c66dSSepherosa Ziehau 
938433e5f5SSepherosa Ziehau /* Tunable params */
948892ea20SAggelos Economopoulos static int mxge_nvidia_ecrc_enable = 1;
958892ea20SAggelos Economopoulos static int mxge_force_firmware = 0;
967cc92483SSepherosa Ziehau static int mxge_intr_coal_delay = MXGE_INTR_COAL_DELAY;
978892ea20SAggelos Economopoulos static int mxge_deassert_wait = 1;
988892ea20SAggelos Economopoulos static int mxge_ticks;
9938b8e77fSSepherosa Ziehau static int mxge_num_slices = 0;
1008892ea20SAggelos Economopoulos static int mxge_always_promisc = 0;
10189d55360SSepherosa Ziehau static int mxge_throttle = 0;
1027cc92483SSepherosa Ziehau static int mxge_msi_enable = 1;
103e6c7b753SSepherosa Ziehau static int mxge_msix_enable = 1;
104aca8f373SSepherosa Ziehau static int mxge_multi_tx = 1;
1058433e5f5SSepherosa Ziehau /*
1068433e5f5SSepherosa Ziehau  * Don't use RSS by default, its just too slow
1078433e5f5SSepherosa Ziehau  */
1088433e5f5SSepherosa Ziehau static int mxge_use_rss = 0;
1097cc92483SSepherosa Ziehau 
11000f2de12SSepherosa Ziehau static char mxge_flowctrl[IFM_ETH_FC_STRLEN] = IFM_ETH_FC_FORCE_FULL;
11100f2de12SSepherosa Ziehau 
112c7431c78SSepherosa Ziehau static const char *mxge_fw_unaligned = "mxge_ethp_z8e";
113c7431c78SSepherosa Ziehau static const char *mxge_fw_aligned = "mxge_eth_z8e";
114c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
115c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
1168892ea20SAggelos Economopoulos 
117e6c7b753SSepherosa Ziehau TUNABLE_INT("hw.mxge.num_slices", &mxge_num_slices);
1187cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.intr_coal_delay", &mxge_intr_coal_delay);
1197cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.nvidia_ecrc_enable", &mxge_nvidia_ecrc_enable);
1207cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.force_firmware", &mxge_force_firmware);
1217cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.deassert_wait", &mxge_deassert_wait);
1227cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.ticks", &mxge_ticks);
1237cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.always_promisc", &mxge_always_promisc);
1247cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.throttle", &mxge_throttle);
125aca8f373SSepherosa Ziehau TUNABLE_INT("hw.mxge.multi_tx", &mxge_multi_tx);
1268433e5f5SSepherosa Ziehau TUNABLE_INT("hw.mxge.use_rss", &mxge_use_rss);
1277cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.msi.enable", &mxge_msi_enable);
128e6c7b753SSepherosa Ziehau TUNABLE_INT("hw.mxge.msix.enable", &mxge_msix_enable);
12900f2de12SSepherosa Ziehau TUNABLE_STR("hw.mxge.flow_ctrl", mxge_flowctrl, sizeof(mxge_flowctrl));
1307cc92483SSepherosa Ziehau 
1318892ea20SAggelos Economopoulos static int mxge_probe(device_t dev);
1328892ea20SAggelos Economopoulos static int mxge_attach(device_t dev);
1338892ea20SAggelos Economopoulos static int mxge_detach(device_t dev);
1348892ea20SAggelos Economopoulos static int mxge_shutdown(device_t dev);
1358892ea20SAggelos Economopoulos 
136e6c7b753SSepherosa Ziehau static int mxge_alloc_intr(struct mxge_softc *sc);
137e6c7b753SSepherosa Ziehau static void mxge_free_intr(struct mxge_softc *sc);
138e6c7b753SSepherosa Ziehau static int mxge_setup_intr(struct mxge_softc *sc);
139e6c7b753SSepherosa Ziehau static void mxge_teardown_intr(struct mxge_softc *sc, int cnt);
140e6c7b753SSepherosa Ziehau 
14189d55360SSepherosa Ziehau static device_method_t mxge_methods[] = {
1428892ea20SAggelos Economopoulos 	/* Device interface */
1438892ea20SAggelos Economopoulos 	DEVMETHOD(device_probe, mxge_probe),
1448892ea20SAggelos Economopoulos 	DEVMETHOD(device_attach, mxge_attach),
1458892ea20SAggelos Economopoulos 	DEVMETHOD(device_detach, mxge_detach),
1468892ea20SAggelos Economopoulos 	DEVMETHOD(device_shutdown, mxge_shutdown),
147d3c9c58eSSascha Wildner 	DEVMETHOD_END
1488892ea20SAggelos Economopoulos };
1498892ea20SAggelos Economopoulos 
15089d55360SSepherosa Ziehau static driver_t mxge_driver = {
1518892ea20SAggelos Economopoulos 	"mxge",
1528892ea20SAggelos Economopoulos 	mxge_methods,
1538892ea20SAggelos Economopoulos 	sizeof(mxge_softc_t),
1548892ea20SAggelos Economopoulos };
1558892ea20SAggelos Economopoulos 
1568892ea20SAggelos Economopoulos static devclass_t mxge_devclass;
1578892ea20SAggelos Economopoulos 
1588892ea20SAggelos Economopoulos /* Declare ourselves to be a child of the PCI bus.*/
159aa2b9d05SSascha Wildner DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, NULL, NULL);
1608892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, firmware, 1, 1, 1);
1618892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, zlib, 1, 1, 1);
1628892ea20SAggelos Economopoulos 
1638892ea20SAggelos Economopoulos static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1648892ea20SAggelos Economopoulos static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1652c29ffc6SSepherosa Ziehau static void mxge_close(mxge_softc_t *sc, int down);
1668892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1678892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
168ca8ca004SSepherosa Ziehau static void mxge_watchdog_reset(mxge_softc_t *sc);
169ca8ca004SSepherosa Ziehau static void mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice);
1708892ea20SAggelos Economopoulos 
1718892ea20SAggelos Economopoulos static int
1728892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1738892ea20SAggelos Economopoulos {
17424e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
17524e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
17624e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
17724e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1788892ea20SAggelos Economopoulos 
1798892ea20SAggelos Economopoulos 		switch (rev) {
1808892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1818892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1828892ea20SAggelos Economopoulos 			break;
1838892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1848892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1858892ea20SAggelos Economopoulos 			break;
1868892ea20SAggelos Economopoulos 		default:
1878892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
18824e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1898892ea20SAggelos Economopoulos 			break;
1908892ea20SAggelos Economopoulos 		}
1918892ea20SAggelos Economopoulos 		return 0;
1928892ea20SAggelos Economopoulos 	}
1938892ea20SAggelos Economopoulos 	return ENXIO;
1948892ea20SAggelos Economopoulos }
1958892ea20SAggelos Economopoulos 
1968892ea20SAggelos Economopoulos static void
1978892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1988892ea20SAggelos Economopoulos {
19989d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
2008892ea20SAggelos Economopoulos 	vm_offset_t len;
2018892ea20SAggelos Economopoulos 
2028892ea20SAggelos Economopoulos 	sc->wc = 1;
2038892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
20489d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
20589d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
2069eb279beSAggelos Economopoulos #endif
2078892ea20SAggelos Economopoulos }
2088892ea20SAggelos Economopoulos 
2098892ea20SAggelos Economopoulos static int
2107cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
2118892ea20SAggelos Economopoulos     bus_size_t alignment)
2128892ea20SAggelos Economopoulos {
2137cc92483SSepherosa Ziehau 	bus_size_t boundary;
2148892ea20SAggelos Economopoulos 	int err;
2158892ea20SAggelos Economopoulos 
2167cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
2178892ea20SAggelos Economopoulos 		boundary = 0;
2187cc92483SSepherosa Ziehau 	else
2198892ea20SAggelos Economopoulos 		boundary = 4096;
2208892ea20SAggelos Economopoulos 
2217cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
2227cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
2237cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2248892ea20SAggelos Economopoulos 	if (err != 0) {
2257cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2268892ea20SAggelos Economopoulos 		return err;
2278892ea20SAggelos Economopoulos 	}
2288892ea20SAggelos Economopoulos 	return 0;
2298892ea20SAggelos Economopoulos }
2308892ea20SAggelos Economopoulos 
2318892ea20SAggelos Economopoulos static void
2327cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2338892ea20SAggelos Economopoulos {
2347cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2357cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2367cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2378892ea20SAggelos Economopoulos }
2388892ea20SAggelos Economopoulos 
2398892ea20SAggelos Economopoulos /*
2408892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2418892ea20SAggelos Economopoulos  * SN=x\0
2428892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2438892ea20SAggelos Economopoulos  * PC=text\0
2448892ea20SAggelos Economopoulos  */
2458892ea20SAggelos Economopoulos static int
2468892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2478892ea20SAggelos Economopoulos {
248c7431c78SSepherosa Ziehau 	const char *ptr;
24989d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
25089d55360SSepherosa Ziehau 	char *endptr;
2518892ea20SAggelos Economopoulos 
2528892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2538892ea20SAggelos Economopoulos 	found_mac = 0;
25489d55360SSepherosa Ziehau 	found_sn2 = 0;
25589d55360SSepherosa Ziehau 	while (*ptr != '\0') {
25689d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
25789d55360SSepherosa Ziehau 			ptr += 4;
25889d55360SSepherosa Ziehau 			for (i = 0;;) {
25989d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
26089d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2618892ea20SAggelos Economopoulos 					goto abort;
26289d55360SSepherosa Ziehau 				ptr = endptr;
26389d55360SSepherosa Ziehau 				if (++i == 6)
26489d55360SSepherosa Ziehau 					break;
26589d55360SSepherosa Ziehau 				if (*ptr++ != ':')
26689d55360SSepherosa Ziehau 					goto abort;
26789d55360SSepherosa Ziehau 			}
2688892ea20SAggelos Economopoulos 			found_mac = 1;
26989d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2708892ea20SAggelos Economopoulos 			ptr += 3;
27189d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
27289d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
27389d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2748892ea20SAggelos Economopoulos 			ptr += 3;
27589d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
27689d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
27789d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
27889d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
27989d55360SSepherosa Ziehau 			ptr += 4;
28089d55360SSepherosa Ziehau 			found_sn2 = 1;
28189d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
28289d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2838892ea20SAggelos Economopoulos 		}
28489d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2858892ea20SAggelos Economopoulos 	}
2868892ea20SAggelos Economopoulos 
2878892ea20SAggelos Economopoulos 	if (found_mac)
2888892ea20SAggelos Economopoulos 		return 0;
2898892ea20SAggelos Economopoulos 
2908892ea20SAggelos Economopoulos abort:
2918892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2928892ea20SAggelos Economopoulos 	return ENXIO;
2938892ea20SAggelos Economopoulos }
2948892ea20SAggelos Economopoulos 
29589d55360SSepherosa Ziehau #if defined(__i386__) || defined(__x86_64__)
29689d55360SSepherosa Ziehau 
2978892ea20SAggelos Economopoulos static void
2988892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
2998892ea20SAggelos Economopoulos {
3008892ea20SAggelos Economopoulos 	uint32_t val;
3018892ea20SAggelos Economopoulos 	unsigned long base, off;
3028892ea20SAggelos Economopoulos 	char *va, *cfgptr;
3038892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
3048892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
3058892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
3068892ea20SAggelos Economopoulos 	uint32_t *ptr32;
3078892ea20SAggelos Economopoulos 
3088892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
3098892ea20SAggelos Economopoulos 		return;
3108892ea20SAggelos Economopoulos 
3118892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
3128892ea20SAggelos Economopoulos 	if (pdev == NULL) {
3138892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
3148892ea20SAggelos Economopoulos 		return;
3158892ea20SAggelos Economopoulos 	}
3168892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3178892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3188892ea20SAggelos Economopoulos 
3198892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
3208892ea20SAggelos Economopoulos 		return;
3218892ea20SAggelos Economopoulos 
3228892ea20SAggelos Economopoulos 	base = 0;
3238892ea20SAggelos Economopoulos 
3248892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3258892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3268892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3278892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3288892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3298892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3308892ea20SAggelos Economopoulos 		if (mcp55 &&
3318892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3328892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3338892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3348892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3358892ea20SAggelos Economopoulos 		}
3368892ea20SAggelos Economopoulos 	}
3378892ea20SAggelos Economopoulos 	if (!base)
3388892ea20SAggelos Economopoulos 		return;
3398892ea20SAggelos Economopoulos 
3407cc92483SSepherosa Ziehau 	/*
3417cc92483SSepherosa Ziehau 	 * XXXX
3427cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3437cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3447cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3457cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3462f47b54fSSepherosa Ziehau 	 * DragonFly grows support for extended pcie config space access.
3478892ea20SAggelos Economopoulos 	 */
3488892ea20SAggelos Economopoulos #if 0
3497cc92483SSepherosa Ziehau 	/*
3507cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3517cc92483SSepherosa Ziehau 	 * config space
3527cc92483SSepherosa Ziehau 	 */
3538892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3548892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3558892ea20SAggelos Economopoulos 		val |= 0x40;
3568892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3578892ea20SAggelos Economopoulos 		return;
3588892ea20SAggelos Economopoulos 	}
3598892ea20SAggelos Economopoulos #endif
3607cc92483SSepherosa Ziehau 	/*
3617cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3628892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3638892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3648892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3658892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3668892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3678892ea20SAggelos Economopoulos 	 */
3688892ea20SAggelos Economopoulos 
3698892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3708892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3718892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3728892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3738892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3748892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3758892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3768892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3778892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3788892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3798892ea20SAggelos Economopoulos 
3807cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3817cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3828892ea20SAggelos Economopoulos 
3838892ea20SAggelos Economopoulos 	/* map it into the kernel */
3848892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3858892ea20SAggelos Economopoulos 	if (va == NULL) {
3868892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3878892ea20SAggelos Economopoulos 		return;
3888892ea20SAggelos Economopoulos 	}
3898892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3908892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3918892ea20SAggelos Economopoulos 
3928892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3938892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3948892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3958892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3968892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3978892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3988892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
3998892ea20SAggelos Economopoulos 		return;
4008892ea20SAggelos Economopoulos 	}
4018892ea20SAggelos Economopoulos 
4028892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
4038892ea20SAggelos Economopoulos 	val = *ptr32;
4048892ea20SAggelos Economopoulos 
4058892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
4068892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
4078892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4088892ea20SAggelos Economopoulos 		return;
4098892ea20SAggelos Economopoulos 	}
4108892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
4118892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4127cc92483SSepherosa Ziehau 	if (bootverbose) {
4137cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
4147cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
4158892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
4167cc92483SSepherosa Ziehau 	}
4178892ea20SAggelos Economopoulos }
41889d55360SSepherosa Ziehau 
41989d55360SSepherosa Ziehau #else	/* __i386__ || __x86_64__ */
42089d55360SSepherosa Ziehau 
4218892ea20SAggelos Economopoulos static void
4228892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
4238892ea20SAggelos Economopoulos {
4247cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4258892ea20SAggelos Economopoulos }
4268892ea20SAggelos Economopoulos 
42789d55360SSepherosa Ziehau #endif
4288892ea20SAggelos Economopoulos 
4298892ea20SAggelos Economopoulos static int
4308892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4318892ea20SAggelos Economopoulos {
4328892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4337cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4348892ea20SAggelos Economopoulos 	int status;
4358892ea20SAggelos Economopoulos 	uint32_t len;
4367cc92483SSepherosa Ziehau 	const char *test = " ";
4378892ea20SAggelos Economopoulos 
4387cc92483SSepherosa Ziehau 	/*
4397cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4408892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4418892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4428892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4438892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4448892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4458892ea20SAggelos Economopoulos 	 * transfers took to complete.
4468892ea20SAggelos Economopoulos 	 */
4478892ea20SAggelos Economopoulos 
4488892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4498892ea20SAggelos Economopoulos 
4508892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4518892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4528892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4538892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4548892ea20SAggelos Economopoulos 	if (status != 0) {
4558892ea20SAggelos Economopoulos 		test = "read";
4568892ea20SAggelos Economopoulos 		goto abort;
4578892ea20SAggelos Economopoulos 	}
4587cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4597cc92483SSepherosa Ziehau 
4608892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4618892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4628892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4638892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4648892ea20SAggelos Economopoulos 	if (status != 0) {
4658892ea20SAggelos Economopoulos 		test = "write";
4668892ea20SAggelos Economopoulos 		goto abort;
4678892ea20SAggelos Economopoulos 	}
4687cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4698892ea20SAggelos Economopoulos 
4708892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4718892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4728892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4738892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4748892ea20SAggelos Economopoulos 	if (status != 0) {
4758892ea20SAggelos Economopoulos 		test = "read/write";
4768892ea20SAggelos Economopoulos 		goto abort;
4778892ea20SAggelos Economopoulos 	}
4788892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4798892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4808892ea20SAggelos Economopoulos 
4818892ea20SAggelos Economopoulos abort:
4827cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4838892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4848892ea20SAggelos Economopoulos 		    test, status);
4857cc92483SSepherosa Ziehau 	}
4868892ea20SAggelos Economopoulos 	return status;
4878892ea20SAggelos Economopoulos }
4888892ea20SAggelos Economopoulos 
4898892ea20SAggelos Economopoulos /*
4908892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4918892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4928892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4938892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4948892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4958892ea20SAggelos Economopoulos  *
4968892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4978892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4988892ea20SAggelos Economopoulos  *
4998892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
5008892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
5018892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
5028892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
5038892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
5048892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
5058892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
5068892ea20SAggelos Economopoulos  */
5078892ea20SAggelos Economopoulos static int
5088892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
5098892ea20SAggelos Economopoulos {
5108892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
5118892ea20SAggelos Economopoulos 	int reg, status;
5128892ea20SAggelos Economopoulos 	uint16_t pectl;
5138892ea20SAggelos Economopoulos 
5148892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
5157cc92483SSepherosa Ziehau 
5168892ea20SAggelos Economopoulos 	/*
5178892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
5188892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
5198892ea20SAggelos Economopoulos 	 */
5208892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5218892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
5228892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
5237cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5248892ea20SAggelos Economopoulos 			    pectl);
5258892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5268892ea20SAggelos Economopoulos 		}
5278892ea20SAggelos Economopoulos 	}
5288892ea20SAggelos Economopoulos 
5298892ea20SAggelos Economopoulos 	/*
5307cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5318892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5328892ea20SAggelos Economopoulos 	 */
5338892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5348892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5357cc92483SSepherosa Ziehau 	if (status != 0)
5368892ea20SAggelos Economopoulos 		return status;
5378892ea20SAggelos Economopoulos 
5388892ea20SAggelos Economopoulos 	/*
5398892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5408892ea20SAggelos Economopoulos 	 */
5418892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5428892ea20SAggelos Economopoulos 
5438892ea20SAggelos Economopoulos 	/*
5448892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
54589d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5468892ea20SAggelos Economopoulos 	 */
54789d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
54889d55360SSepherosa Ziehau 		return 0;
5498892ea20SAggelos Economopoulos 
5508892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5518892ea20SAggelos Economopoulos 	if (status == 0)
5528892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5538892ea20SAggelos Economopoulos 
5548892ea20SAggelos Economopoulos 	if (status != E2BIG)
5558892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5567cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5578892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5588892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5597cc92483SSepherosa Ziehau 	}
5608892ea20SAggelos Economopoulos 	return status;
5618892ea20SAggelos Economopoulos }
5628892ea20SAggelos Economopoulos 
5638892ea20SAggelos Economopoulos static int
5648892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5658892ea20SAggelos Economopoulos {
5668892ea20SAggelos Economopoulos 	int aligned = 0;
56789d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5688892ea20SAggelos Economopoulos 
56989d55360SSepherosa Ziehau 	if (sc->throttle)
57089d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5718892ea20SAggelos Economopoulos 
57289d55360SSepherosa Ziehau 	if (force_firmware != 0) {
57389d55360SSepherosa Ziehau 		if (force_firmware == 1)
5748892ea20SAggelos Economopoulos 			aligned = 1;
5758892ea20SAggelos Economopoulos 		else
5768892ea20SAggelos Economopoulos 			aligned = 0;
5777cc92483SSepherosa Ziehau 		if (bootverbose) {
5788892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5798892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5808892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5817cc92483SSepherosa Ziehau 		}
5828892ea20SAggelos Economopoulos 		goto abort;
5838892ea20SAggelos Economopoulos 	}
5848892ea20SAggelos Economopoulos 
5857cc92483SSepherosa Ziehau 	/*
5867cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5877cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5887cc92483SSepherosa Ziehau 	 */
5898892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5907cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5917cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5928892ea20SAggelos Economopoulos 		aligned = 1;
5938892ea20SAggelos Economopoulos 		goto abort;
5948892ea20SAggelos Economopoulos 	}
5958892ea20SAggelos Economopoulos 
5967cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5978892ea20SAggelos Economopoulos 		return 0;
5988892ea20SAggelos Economopoulos 
5998892ea20SAggelos Economopoulos abort:
6008892ea20SAggelos Economopoulos 	if (aligned) {
6018892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
6028892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
6038892ea20SAggelos Economopoulos 	} else {
6048892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
6058892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
6068892ea20SAggelos Economopoulos 	}
6077cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
6088892ea20SAggelos Economopoulos }
6098892ea20SAggelos Economopoulos 
6108892ea20SAggelos Economopoulos static int
6118892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6128892ea20SAggelos Economopoulos {
6138892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6148a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware type: 0x%x\n",
6158892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
6168892ea20SAggelos Economopoulos 		return EIO;
6178892ea20SAggelos Economopoulos 	}
6188892ea20SAggelos Economopoulos 
6197cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
6207cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6217cc92483SSepherosa Ziehau 	if (bootverbose)
6228a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "firmware id: %s\n", hdr->version);
6238892ea20SAggelos Economopoulos 
624b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6258892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6268892ea20SAggelos Economopoulos 
6277cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6287cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6298a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Found firmware version %s\n",
6308892ea20SAggelos Economopoulos 		    sc->fw_version);
6318a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Driver needs %d.%d\n",
6328892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6338892ea20SAggelos Economopoulos 		return EINVAL;
6348892ea20SAggelos Economopoulos 	}
6358892ea20SAggelos Economopoulos 	return 0;
6368892ea20SAggelos Economopoulos }
6378892ea20SAggelos Economopoulos 
6388892ea20SAggelos Economopoulos static void *
6398892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6408892ea20SAggelos Economopoulos {
6417cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6428892ea20SAggelos Economopoulos }
6438892ea20SAggelos Economopoulos 
6448892ea20SAggelos Economopoulos static void
6458892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6468892ea20SAggelos Economopoulos {
647d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6488892ea20SAggelos Economopoulos }
649d83c779aSSascha Wildner 
6508892ea20SAggelos Economopoulos static int
6518892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6528892ea20SAggelos Economopoulos {
653d83c779aSSascha Wildner 	z_stream zs;
654d83c779aSSascha Wildner 	char *inflate_buffer;
655d83c779aSSascha Wildner 	const struct firmware *fw;
6568892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6578892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6588892ea20SAggelos Economopoulos 	int status;
6598892ea20SAggelos Economopoulos 	unsigned int i;
66089d55360SSepherosa Ziehau 	char dummy;
6618892ea20SAggelos Economopoulos 	size_t fw_len;
6628892ea20SAggelos Economopoulos 
663d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6648892ea20SAggelos Economopoulos 	if (fw == NULL) {
6658a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Could not find firmware image %s\n",
6668892ea20SAggelos Economopoulos 		    sc->fw_name);
6678892ea20SAggelos Economopoulos 		return ENOENT;
6688892ea20SAggelos Economopoulos 	}
669d83c779aSSascha Wildner 
6707cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6718892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6728892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6738892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6748892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6758892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6768892ea20SAggelos Economopoulos 		status = EIO;
6778892ea20SAggelos Economopoulos 		goto abort_with_fw;
6788892ea20SAggelos Economopoulos 	}
6798892ea20SAggelos Economopoulos 
6807cc92483SSepherosa Ziehau 	/*
6817cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6827cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6837cc92483SSepherosa Ziehau 	 */
6848892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6857cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6868892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6878892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6888892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6898892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6908892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6918892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6928a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "zlib %d\n", status);
6938892ea20SAggelos Economopoulos 		status = EIO;
6948892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6958892ea20SAggelos Economopoulos 	}
696d83c779aSSascha Wildner 
6977cc92483SSepherosa Ziehau 	/* Check id */
6987cc92483SSepherosa Ziehau 	hdr_offset =
6997cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
7008892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
7018a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware file");
7028892ea20SAggelos Economopoulos 		status = EIO;
703d83c779aSSascha Wildner 		goto abort_with_buffer;
7048892ea20SAggelos Economopoulos 	}
705d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
7068892ea20SAggelos Economopoulos 
7078892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
7088892ea20SAggelos Economopoulos 	if (status != 0)
709d83c779aSSascha Wildner 		goto abort_with_buffer;
7108892ea20SAggelos Economopoulos 
7118892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
7128892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
7137cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
7148892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
7158892ea20SAggelos Economopoulos 		wmb();
71689d55360SSepherosa Ziehau 		dummy = *sc->sram;
7178892ea20SAggelos Economopoulos 		wmb();
7188892ea20SAggelos Economopoulos 	}
7198892ea20SAggelos Economopoulos 
7208892ea20SAggelos Economopoulos 	*limit = fw_len;
7218892ea20SAggelos Economopoulos 	status = 0;
7228892ea20SAggelos Economopoulos abort_with_buffer:
723d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7248892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7258892ea20SAggelos Economopoulos abort_with_fw:
726d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7278892ea20SAggelos Economopoulos 	return status;
7288892ea20SAggelos Economopoulos }
7298892ea20SAggelos Economopoulos 
7308892ea20SAggelos Economopoulos /*
7318892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7328892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7338892ea20SAggelos Economopoulos  */
7348892ea20SAggelos Economopoulos static void
7358892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7368892ea20SAggelos Economopoulos {
7378892ea20SAggelos Economopoulos 	char buf_bytes[72];
7388892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7398892ea20SAggelos Economopoulos 	volatile char *submit;
7408892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7418892ea20SAggelos Economopoulos 	int i;
7428892ea20SAggelos Economopoulos 
7438892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7448892ea20SAggelos Economopoulos 
7457cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7468892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7478892ea20SAggelos Economopoulos 	*confirm = 0;
7488892ea20SAggelos Economopoulos 	wmb();
7498892ea20SAggelos Economopoulos 
7507cc92483SSepherosa Ziehau 	/*
7517cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7527cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7537cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7548892ea20SAggelos Economopoulos 	 */
7557cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7567cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7578892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7588892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7598892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7607cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7617cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7628892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7638892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7648892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7658892ea20SAggelos Economopoulos 
7668892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7678892ea20SAggelos Economopoulos 
7688892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7698892ea20SAggelos Economopoulos 	wmb();
7708892ea20SAggelos Economopoulos 	DELAY(1000);
7718892ea20SAggelos Economopoulos 	wmb();
7728892ea20SAggelos Economopoulos 	i = 0;
7738892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7748892ea20SAggelos Economopoulos 		DELAY(1000);
7758892ea20SAggelos Economopoulos 		i++;
7768892ea20SAggelos Economopoulos 	}
7778892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7786ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7797cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7808892ea20SAggelos Economopoulos 	}
7818892ea20SAggelos Economopoulos }
7828892ea20SAggelos Economopoulos 
7838892ea20SAggelos Economopoulos static int
7848892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7858892ea20SAggelos Economopoulos {
7868892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7878892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7888892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7898892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7908892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7918892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7928892ea20SAggelos Economopoulos 
7936ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7948892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7958892ea20SAggelos Economopoulos 
7968892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7978892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7988892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
7998892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
8007cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
8017cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
8028892ea20SAggelos Economopoulos 
8038892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
8048892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
8052e8181d0SAggelos Economopoulos 
8068892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
8078892ea20SAggelos Economopoulos 	wmb();
8088892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
8098892ea20SAggelos Economopoulos 
8106ee6cba3SSepherosa Ziehau 	/*
8116ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
8126ee6cba3SSepherosa Ziehau 	 */
8138892ea20SAggelos Economopoulos 	err = EAGAIN;
8148892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
8158892ea20SAggelos Economopoulos 		wmb();
8168892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
8178892ea20SAggelos Economopoulos 		case 0:
8188892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
8198892ea20SAggelos Economopoulos 			err = 0;
8208892ea20SAggelos Economopoulos 			break;
8218892ea20SAggelos Economopoulos 		case 0xffffffff:
8228892ea20SAggelos Economopoulos 			DELAY(1000);
8238892ea20SAggelos Economopoulos 			break;
8248892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8258892ea20SAggelos Economopoulos 			err = ENOSYS;
8268892ea20SAggelos Economopoulos 			break;
8278892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8288892ea20SAggelos Economopoulos 			err = E2BIG;
8298892ea20SAggelos Economopoulos 			break;
8308892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8318892ea20SAggelos Economopoulos 			err = EBUSY;
8328892ea20SAggelos Economopoulos 			break;
83389d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
83489d55360SSepherosa Ziehau 			err = ENXIO;
83589d55360SSepherosa Ziehau 			break;
8368892ea20SAggelos Economopoulos 		default:
8376ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8388892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8398892ea20SAggelos Economopoulos 			err = ENXIO;
8408892ea20SAggelos Economopoulos 			break;
8418892ea20SAggelos Economopoulos 		}
8428892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8438892ea20SAggelos Economopoulos 			break;
8448892ea20SAggelos Economopoulos 	}
8456ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8466ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8478892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8486ee6cba3SSepherosa Ziehau 	}
8498892ea20SAggelos Economopoulos 	return err;
8508892ea20SAggelos Economopoulos }
8518892ea20SAggelos Economopoulos 
8528892ea20SAggelos Economopoulos static int
8538892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8548892ea20SAggelos Economopoulos {
8558892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8568892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8578892ea20SAggelos Economopoulos 	size_t hdr_offset;
8588892ea20SAggelos Economopoulos 	int status;
8598892ea20SAggelos Economopoulos 
8607cc92483SSepherosa Ziehau 	/*
8617cc92483SSepherosa Ziehau 	 * Find running firmware header
8627cc92483SSepherosa Ziehau 	 */
8637cc92483SSepherosa Ziehau 	hdr_offset =
8647cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8658892ea20SAggelos Economopoulos 
8668892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8678a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Running firmware has bad header offset "
8688a20b038SSepherosa Ziehau 		    "(%zu)\n", hdr_offset);
8698892ea20SAggelos Economopoulos 		return EIO;
8708892ea20SAggelos Economopoulos 	}
8718892ea20SAggelos Economopoulos 
8727cc92483SSepherosa Ziehau 	/*
8737cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8747cc92483SSepherosa Ziehau 	 * validate firmware
8757cc92483SSepherosa Ziehau 	 */
8767cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8778892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8787cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8798892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
880d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8818892ea20SAggelos Economopoulos 
8828892ea20SAggelos Economopoulos 	/*
8837cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8848892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8858892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8868892ea20SAggelos Economopoulos 	 */
8878892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8888892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8898892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8908a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Adopting fw %d.%d.%d: "
8918892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8927cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8938892ea20SAggelos Economopoulos 	}
8948892ea20SAggelos Economopoulos 
8958892ea20SAggelos Economopoulos 	return status;
8968892ea20SAggelos Economopoulos }
8978892ea20SAggelos Economopoulos 
8988892ea20SAggelos Economopoulos static int
8998892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
9008892ea20SAggelos Economopoulos {
9018892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
9028892ea20SAggelos Economopoulos 	volatile char *submit;
9038892ea20SAggelos Economopoulos 	char buf_bytes[72];
9048892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
9058892ea20SAggelos Economopoulos 	int status, i;
9068892ea20SAggelos Economopoulos 
9078892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
9088892ea20SAggelos Economopoulos 
9098892ea20SAggelos Economopoulos 	size = sc->sram_size;
9108892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
9118892ea20SAggelos Economopoulos 	if (status) {
9128892ea20SAggelos Economopoulos 		if (!adopt)
9138892ea20SAggelos Economopoulos 			return status;
9147cc92483SSepherosa Ziehau 
9157cc92483SSepherosa Ziehau 		/*
9167cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
9177cc92483SSepherosa Ziehau 		 * it is new enough
9187cc92483SSepherosa Ziehau 		 */
9198892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
9208892ea20SAggelos Economopoulos 		if (status) {
9218a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9228892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
9238892ea20SAggelos Economopoulos 			return status;
9248892ea20SAggelos Economopoulos 		}
9258a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Successfully adopted running firmware\n");
9267cc92483SSepherosa Ziehau 
9278892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9288a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9297cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9307cc92483SSepherosa Ziehau 			     "For optimal\n");
9318a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "performance consider loading "
9327cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9338892ea20SAggelos Economopoulos 		}
9348892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9358892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9368892ea20SAggelos Economopoulos 		return 0;
9378892ea20SAggelos Economopoulos 	}
9387cc92483SSepherosa Ziehau 
9397cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9408892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9418892ea20SAggelos Economopoulos 	*confirm = 0;
9428892ea20SAggelos Economopoulos 	wmb();
9437cc92483SSepherosa Ziehau 
9447cc92483SSepherosa Ziehau 	/*
9457cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9467cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9477cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9488892ea20SAggelos Economopoulos 	 */
9498892ea20SAggelos Economopoulos 
9507cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9517cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9528892ea20SAggelos Economopoulos 
9538892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9548892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9558892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9568892ea20SAggelos Economopoulos 
9577cc92483SSepherosa Ziehau 	/*
9587cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9597cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9607cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9618892ea20SAggelos Economopoulos 	 */
9628892ea20SAggelos Economopoulos 					/* where the code starts*/
9638892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9648892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9658892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9668892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9678892ea20SAggelos Economopoulos 
9688892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9698892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9708892ea20SAggelos Economopoulos 	wmb();
9718892ea20SAggelos Economopoulos 	DELAY(1000);
9728892ea20SAggelos Economopoulos 	wmb();
9738892ea20SAggelos Economopoulos 	i = 0;
9748892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9758892ea20SAggelos Economopoulos 		DELAY(1000*10);
9768892ea20SAggelos Economopoulos 		i++;
9778892ea20SAggelos Economopoulos 	}
9788892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9798a20b038SSepherosa Ziehau 		if_printf(sc->ifp,"handoff failed (%p = 0x%x)",
9808892ea20SAggelos Economopoulos 		    confirm, *confirm);
9818892ea20SAggelos Economopoulos 		return ENXIO;
9828892ea20SAggelos Economopoulos 	}
9838892ea20SAggelos Economopoulos 	return 0;
9848892ea20SAggelos Economopoulos }
9858892ea20SAggelos Economopoulos 
9868892ea20SAggelos Economopoulos static int
9878892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9888892ea20SAggelos Economopoulos {
9898892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9908892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9918892ea20SAggelos Economopoulos 
9927cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9937cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9947cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9957cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9968892ea20SAggelos Economopoulos }
9978892ea20SAggelos Economopoulos 
9988892ea20SAggelos Economopoulos static int
9998892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
10008892ea20SAggelos Economopoulos {
10018892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10028892ea20SAggelos Economopoulos 	int status;
10038892ea20SAggelos Economopoulos 
1004*57e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
10058892ea20SAggelos Economopoulos 	if (pause)
10067cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
10078892ea20SAggelos Economopoulos 	else
10087cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
10098892ea20SAggelos Economopoulos 	if (status) {
10105a637e78SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set flow control mode\n");
10118892ea20SAggelos Economopoulos 		return ENXIO;
10128892ea20SAggelos Economopoulos 	}
10138892ea20SAggelos Economopoulos 	sc->pause = pause;
10148892ea20SAggelos Economopoulos 	return 0;
10158892ea20SAggelos Economopoulos }
10168892ea20SAggelos Economopoulos 
10178892ea20SAggelos Economopoulos static void
10188892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
10198892ea20SAggelos Economopoulos {
10208892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10218892ea20SAggelos Economopoulos 	int status;
10228892ea20SAggelos Economopoulos 
1023*57e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* avoid gcc warning */
10248892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10258892ea20SAggelos Economopoulos 		promisc = 1;
10268892ea20SAggelos Economopoulos 
10278892ea20SAggelos Economopoulos 	if (promisc)
10287cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10298892ea20SAggelos Economopoulos 	else
10307cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10317cc92483SSepherosa Ziehau 	if (status)
1032af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set promisc mode\n");
10338892ea20SAggelos Economopoulos }
10348892ea20SAggelos Economopoulos 
10358892ea20SAggelos Economopoulos static void
10368892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10378892ea20SAggelos Economopoulos {
10388892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10398892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10408892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10418892ea20SAggelos Economopoulos 	int err;
10428892ea20SAggelos Economopoulos 
10438892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10448892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10458892ea20SAggelos Economopoulos 		return;
10468892ea20SAggelos Economopoulos 
10478892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
1048*57e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
10498892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10508892ea20SAggelos Economopoulos 	if (err != 0) {
1051af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_ENABLE_ALLMULTI, "
10528892ea20SAggelos Economopoulos 		    "error status: %d\n", err);
10538892ea20SAggelos Economopoulos 		return;
10548892ea20SAggelos Economopoulos 	}
10558892ea20SAggelos Economopoulos 
10568892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10578892ea20SAggelos Economopoulos 		return;
10588892ea20SAggelos Economopoulos 
10597cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10607cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10618892ea20SAggelos Economopoulos 		return;
10628892ea20SAggelos Economopoulos 	}
10638892ea20SAggelos Economopoulos 
10647cc92483SSepherosa Ziehau 	/* Flush all the filters */
10657cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10667cc92483SSepherosa Ziehau 	if (err != 0) {
1067af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10687cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10697cc92483SSepherosa Ziehau 		return;
10707cc92483SSepherosa Ziehau 	}
10718892ea20SAggelos Economopoulos 
10727cc92483SSepherosa Ziehau 	/*
10737cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10747cc92483SSepherosa Ziehau 	 */
1075441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10768892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10778892ea20SAggelos Economopoulos 			continue;
10787cc92483SSepherosa Ziehau 
10798892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10808892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10818892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10828892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10838892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10848892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10858892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10868892ea20SAggelos Economopoulos 		if (err != 0) {
1087af85d4d5SSepherosa Ziehau 			if_printf(ifp, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
10887cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10897cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10908892ea20SAggelos Economopoulos 			return;
10918892ea20SAggelos Economopoulos 		}
10928892ea20SAggelos Economopoulos 	}
10937cc92483SSepherosa Ziehau 
10948892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10958892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10968892ea20SAggelos Economopoulos 	if (err != 0) {
1097af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_DISABLE_ALLMULTI, "
10987cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10998892ea20SAggelos Economopoulos 	}
11008892ea20SAggelos Economopoulos }
11018892ea20SAggelos Economopoulos 
110289d55360SSepherosa Ziehau #if 0
11038892ea20SAggelos Economopoulos static int
11048892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
11058892ea20SAggelos Economopoulos {
11068892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11078892ea20SAggelos Economopoulos 	int status;
11088892ea20SAggelos Economopoulos 
11098892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
11108892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11118892ea20SAggelos Economopoulos 
11128892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
11138892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
11148892ea20SAggelos Economopoulos 	cmd.data0 = 0;
11158892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
11168892ea20SAggelos Economopoulos 			       &cmd);
11178892ea20SAggelos Economopoulos 	if (status == 0)
11188892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11198892ea20SAggelos Economopoulos 
11208892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
11218892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
11228892ea20SAggelos Economopoulos }
112389d55360SSepherosa Ziehau #endif
11248892ea20SAggelos Economopoulos 
11258892ea20SAggelos Economopoulos static int
11268892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11278892ea20SAggelos Economopoulos {
11288892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11298892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11308892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11318892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
1132089301c2SSepherosa Ziehau 	int slice, status, rx_intr_size;
11338892ea20SAggelos Economopoulos 
11347cc92483SSepherosa Ziehau 	/*
11357cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11367cc92483SSepherosa Ziehau 	 * is alive
11377cc92483SSepherosa Ziehau 	 */
11388892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11398892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11408892ea20SAggelos Economopoulos 	if (status != 0) {
11416ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11428892ea20SAggelos Economopoulos 		return ENXIO;
11438892ea20SAggelos Economopoulos 	}
11448892ea20SAggelos Economopoulos 
11458892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11468892ea20SAggelos Economopoulos 
1147089301c2SSepherosa Ziehau 	/*
1148089301c2SSepherosa Ziehau 	 * Set the intrq size
1149089301c2SSepherosa Ziehau 	 * XXX assume 4byte mcp_slot
1150089301c2SSepherosa Ziehau 	 */
1151089301c2SSepherosa Ziehau 	rx_intr_size = sc->rx_intr_slots * sizeof(mcp_slot_t);
1152089301c2SSepherosa Ziehau 	cmd.data0 = rx_intr_size;
11538892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11548892ea20SAggelos Economopoulos 
11558892ea20SAggelos Economopoulos 	/*
11568892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11578892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11588892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11598892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11608892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11618892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11628892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11638892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11648892ea20SAggelos Economopoulos 	 */
11658892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11667cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11677cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11688892ea20SAggelos Economopoulos 		if (status != 0) {
11696ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11708892ea20SAggelos Economopoulos 			return status;
11718892ea20SAggelos Economopoulos 		}
11727cc92483SSepherosa Ziehau 
11738892ea20SAggelos Economopoulos 		/*
11748892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11758892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11768892ea20SAggelos Economopoulos 		 */
11778892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11788892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1179aca8f373SSepherosa Ziehau 		if (sc->num_tx_rings > 1)
11808892ea20SAggelos Economopoulos 			cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11817cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11828892ea20SAggelos Economopoulos 		if (status != 0) {
11836ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11848892ea20SAggelos Economopoulos 			return status;
11858892ea20SAggelos Economopoulos 		}
11868892ea20SAggelos Economopoulos 	}
11878892ea20SAggelos Economopoulos 
11888892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11898892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11908892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
1191414caf0dSSepherosa Ziehau 			ss = &sc->ss[slice];
1192414caf0dSSepherosa Ziehau 
1193414caf0dSSepherosa Ziehau 			rx_done = &ss->rx_data.rx_done;
1194089301c2SSepherosa Ziehau 			memset(rx_done->entry, 0, rx_intr_size);
1195414caf0dSSepherosa Ziehau 
11967cc92483SSepherosa Ziehau 			cmd.data0 =
1197414caf0dSSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
11987cc92483SSepherosa Ziehau 			cmd.data1 =
1199414caf0dSSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
12008892ea20SAggelos Economopoulos 			cmd.data2 = slice;
12017cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
12028892ea20SAggelos Economopoulos 			    &cmd);
12038892ea20SAggelos Economopoulos 		}
12048892ea20SAggelos Economopoulos 	}
12058892ea20SAggelos Economopoulos 
12067cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
12077cc92483SSepherosa Ziehau 	    &cmd);
12088892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12098892ea20SAggelos Economopoulos 
12108892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12118892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12128892ea20SAggelos Economopoulos 
12137cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
12148892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
12157cc92483SSepherosa Ziehau 
12168892ea20SAggelos Economopoulos 	if (status != 0) {
12176ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
12188892ea20SAggelos Economopoulos 		return status;
12198892ea20SAggelos Economopoulos 	}
12208892ea20SAggelos Economopoulos 
12218892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12228892ea20SAggelos Economopoulos 
12237cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
12247cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
12258892ea20SAggelos Economopoulos 
12268892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
12278892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
12288892ea20SAggelos Economopoulos 
12298892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
12307cc92483SSepherosa Ziehau 
12317cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12329a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.idx = 0;
12338892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12348892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12358892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12368892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12378892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12388892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12399a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.cnt = 0;
12409a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.cnt = 0;
12417cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12427cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12438892ea20SAggelos Economopoulos 	}
12448892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12457cc92483SSepherosa Ziehau 
12468892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12478892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12488892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12498892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12507cc92483SSepherosa Ziehau 
125189d55360SSepherosa Ziehau 	if (sc->throttle) {
125289d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12537cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12546ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
125589d55360SSepherosa Ziehau 	}
12568892ea20SAggelos Economopoulos 	return status;
12578892ea20SAggelos Economopoulos }
12588892ea20SAggelos Economopoulos 
12598892ea20SAggelos Economopoulos static int
126089d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
126189d55360SSepherosa Ziehau {
126289d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
126389d55360SSepherosa Ziehau 	mxge_softc_t *sc;
126489d55360SSepherosa Ziehau 	int err;
126589d55360SSepherosa Ziehau 	unsigned int throttle;
126689d55360SSepherosa Ziehau 
126789d55360SSepherosa Ziehau 	sc = arg1;
126889d55360SSepherosa Ziehau 	throttle = sc->throttle;
126989d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12705a637e78SSepherosa Ziehau 	if (err != 0)
127189d55360SSepherosa Ziehau 		return err;
127289d55360SSepherosa Ziehau 
127389d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
127489d55360SSepherosa Ziehau 		return 0;
127589d55360SSepherosa Ziehau 
127689d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
127789d55360SSepherosa Ziehau 		return EINVAL;
127889d55360SSepherosa Ziehau 
127926634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
128089d55360SSepherosa Ziehau 
128189d55360SSepherosa Ziehau 	cmd.data0 = throttle;
128289d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
128389d55360SSepherosa Ziehau 	if (err == 0)
128489d55360SSepherosa Ziehau 		sc->throttle = throttle;
128589d55360SSepherosa Ziehau 
128626634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
128789d55360SSepherosa Ziehau 	return err;
128889d55360SSepherosa Ziehau }
128989d55360SSepherosa Ziehau 
129089d55360SSepherosa Ziehau static int
12918433e5f5SSepherosa Ziehau mxge_change_use_rss(SYSCTL_HANDLER_ARGS)
12928433e5f5SSepherosa Ziehau {
12938433e5f5SSepherosa Ziehau 	mxge_softc_t *sc;
12948433e5f5SSepherosa Ziehau 	int err, use_rss;
12958433e5f5SSepherosa Ziehau 
12968433e5f5SSepherosa Ziehau 	sc = arg1;
12978433e5f5SSepherosa Ziehau 	use_rss = sc->use_rss;
12988433e5f5SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &use_rss, arg2, req);
12998433e5f5SSepherosa Ziehau 	if (err != 0)
13008433e5f5SSepherosa Ziehau 		return err;
13018433e5f5SSepherosa Ziehau 
13028433e5f5SSepherosa Ziehau 	if (use_rss == sc->use_rss)
13038433e5f5SSepherosa Ziehau 		return 0;
13048433e5f5SSepherosa Ziehau 
13058433e5f5SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
13068433e5f5SSepherosa Ziehau 
13078433e5f5SSepherosa Ziehau 	sc->use_rss = use_rss;
13088433e5f5SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
13098433e5f5SSepherosa Ziehau 		mxge_close(sc, 0);
13108433e5f5SSepherosa Ziehau 		mxge_open(sc);
13118433e5f5SSepherosa Ziehau 	}
13128433e5f5SSepherosa Ziehau 
13138433e5f5SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13148433e5f5SSepherosa Ziehau 	return err;
13158433e5f5SSepherosa Ziehau }
13168433e5f5SSepherosa Ziehau 
13178433e5f5SSepherosa Ziehau static int
13188892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
13198892ea20SAggelos Economopoulos {
13208892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
13218892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
13228892ea20SAggelos Economopoulos 	int err;
13238892ea20SAggelos Economopoulos 
13248892ea20SAggelos Economopoulos 	sc = arg1;
13258892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
13268892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
13275a637e78SSepherosa Ziehau 	if (err != 0)
13288892ea20SAggelos Economopoulos 		return err;
13295a637e78SSepherosa Ziehau 
13308892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
13318892ea20SAggelos Economopoulos 		return 0;
13328892ea20SAggelos Economopoulos 
13338892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
13348892ea20SAggelos Economopoulos 		return EINVAL;
13358892ea20SAggelos Economopoulos 
133626634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
133789d55360SSepherosa Ziehau 
13388892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
13398892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
13408892ea20SAggelos Economopoulos 
134126634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13428892ea20SAggelos Economopoulos 	return err;
13438892ea20SAggelos Economopoulos }
13448892ea20SAggelos Economopoulos 
13458892ea20SAggelos Economopoulos static int
13468892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13478892ea20SAggelos Economopoulos {
13488892ea20SAggelos Economopoulos 	int err;
13498892ea20SAggelos Economopoulos 
13508892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13518892ea20SAggelos Economopoulos 		return EFAULT;
13528892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13538892ea20SAggelos Economopoulos 	arg1 = NULL;
13548892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13558892ea20SAggelos Economopoulos 
13568892ea20SAggelos Economopoulos 	return err;
13578892ea20SAggelos Economopoulos }
13588892ea20SAggelos Economopoulos 
13598892ea20SAggelos Economopoulos static void
13608892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13618892ea20SAggelos Economopoulos {
1362798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13638892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13648892ea20SAggelos Economopoulos 		int slice;
13658892ea20SAggelos Economopoulos 
13668892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13678892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1368798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13698892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13708892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13718892ea20SAggelos Economopoulos 			}
1372798c3369SSepherosa Ziehau 		}
1373798c3369SSepherosa Ziehau 	}
1374798c3369SSepherosa Ziehau 
1375798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13768892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13778892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1378798c3369SSepherosa Ziehau 	}
1379798c3369SSepherosa Ziehau }
13808892ea20SAggelos Economopoulos 
13818892ea20SAggelos Economopoulos static void
13828892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13838892ea20SAggelos Economopoulos {
13848892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13858892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13868892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13878892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13888892ea20SAggelos Economopoulos 	int slice;
13898892ea20SAggelos Economopoulos 	char slice_num[8];
13908892ea20SAggelos Economopoulos 
139126595b18SSascha Wildner 	ctx = device_get_sysctl_ctx(sc->dev);
139226595b18SSascha Wildner 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
13938892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13948892ea20SAggelos Economopoulos 
13957cc92483SSepherosa Ziehau 	/*
13967cc92483SSepherosa Ziehau 	 * Random information
13977cc92483SSepherosa Ziehau 	 */
13987cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
13997cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
14008892ea20SAggelos Economopoulos 
14017cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
14027cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
14038892ea20SAggelos Economopoulos 
14047cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
14057cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
14068892ea20SAggelos Economopoulos 
14077cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
14087cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
140989d55360SSepherosa Ziehau 
14107cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
14117cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
14128892ea20SAggelos Economopoulos 
14137cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
14147cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
14158892ea20SAggelos Economopoulos 
14167cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
14177cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
14188892ea20SAggelos Economopoulos 
14197cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
14207cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
14218892ea20SAggelos Economopoulos 
14227cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14237cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14247cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14257cc92483SSepherosa Ziehau 
14267cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14277cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14287cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14297cc92483SSepherosa Ziehau 
14307cc92483SSepherosa Ziehau 	/*
14317cc92483SSepherosa Ziehau 	 * Performance related tunables
14327cc92483SSepherosa Ziehau 	 */
14337cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14347cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14357cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14367cc92483SSepherosa Ziehau 
14377cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14387cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14397cc92483SSepherosa Ziehau 	    "Transmit throttling");
14407cc92483SSepherosa Ziehau 
14418433e5f5SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "use_rss",
14428433e5f5SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_use_rss, "I",
14438433e5f5SSepherosa Ziehau 	    "Use RSS");
14448433e5f5SSepherosa Ziehau 
14457cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14467cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14477cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14487cc92483SSepherosa Ziehau 
14497cc92483SSepherosa Ziehau 	/*
14507cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14517cc92483SSepherosa Ziehau 	 * Need to swap it
14527cc92483SSepherosa Ziehau 	 */
14537cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14547cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14557cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14567cc92483SSepherosa Ziehau 
14577cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14587cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14597cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14607cc92483SSepherosa Ziehau 
14617cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14627cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14637cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14647cc92483SSepherosa Ziehau 
14657cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14667cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14677cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14687cc92483SSepherosa Ziehau 
14697cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14707cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14717cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14727cc92483SSepherosa Ziehau 
14737cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14747cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14757cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14767cc92483SSepherosa Ziehau 
14777cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14787cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14797cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14807cc92483SSepherosa Ziehau 
14817cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14827cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14837cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14847cc92483SSepherosa Ziehau 
14857cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14867cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14877cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14887cc92483SSepherosa Ziehau 
14897cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14907cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14917cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14927cc92483SSepherosa Ziehau 
14937cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
14947cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
14957cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
14967cc92483SSepherosa Ziehau 
14977cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
14987cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
14997cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
15007cc92483SSepherosa Ziehau 
15017cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
15027cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
15037cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
15048892ea20SAggelos Economopoulos 
15058892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
15068892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15077cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
15087cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1509798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1510798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1511798c3369SSepherosa Ziehau 		return;
1512798c3369SSepherosa Ziehau 	}
15138892ea20SAggelos Economopoulos 
15148892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
15158892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
15168892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
15178892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
15188892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1519b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
15207cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15217cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1522798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1523798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1524798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1525798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1526798c3369SSepherosa Ziehau 		}
15278892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15287cc92483SSepherosa Ziehau 
15297cc92483SSepherosa Ziehau 		/*
15307cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15317cc92483SSepherosa Ziehau 		 */
15327cc92483SSepherosa Ziehau 
15337cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15349a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_small.cnt, 0, "rx_small_cnt");
15357cc92483SSepherosa Ziehau 
15367cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15379a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_big.cnt, 0, "rx_small_cnt");
15388892ea20SAggelos Economopoulos 
15397cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15407cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15417cc92483SSepherosa Ziehau 
15427cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15437cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15447cc92483SSepherosa Ziehau 
15457cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15467cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15477cc92483SSepherosa Ziehau 
15487cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15497cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15507cc92483SSepherosa Ziehau 
15517cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15527cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15537cc92483SSepherosa Ziehau 
15547cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15557cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15568892ea20SAggelos Economopoulos 	}
15578892ea20SAggelos Economopoulos }
15588892ea20SAggelos Economopoulos 
155989d55360SSepherosa Ziehau /*
156089d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
156189d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
156289d55360SSepherosa Ziehau  */
1563ddbf91b7SSepherosa Ziehau static __inline void
15648892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15658892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15668892ea20SAggelos Economopoulos {
15678892ea20SAggelos Economopoulos 	int idx, starting_slot;
15685ca32f31SSepherosa Ziehau 
15698892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15708892ea20SAggelos Economopoulos 	while (cnt > 1) {
15718892ea20SAggelos Economopoulos 		cnt--;
15728892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15735ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15748892ea20SAggelos Economopoulos 		wmb();
15758892ea20SAggelos Economopoulos 	}
15768892ea20SAggelos Economopoulos }
15778892ea20SAggelos Economopoulos 
15788892ea20SAggelos Economopoulos /*
157989d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15808892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15818892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15828892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15838892ea20SAggelos Economopoulos  */
1584ddbf91b7SSepherosa Ziehau static __inline void
158589d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15868892ea20SAggelos Economopoulos {
15878892ea20SAggelos Economopoulos 	int idx, i;
15888892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15898892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15908892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15918892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15928892ea20SAggelos Economopoulos 	uint8_t last_flags;
15938892ea20SAggelos Economopoulos 
15948892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
15958892ea20SAggelos Economopoulos 
15968892ea20SAggelos Economopoulos 	last_flags = src->flags;
15978892ea20SAggelos Economopoulos 	src->flags = 0;
15988892ea20SAggelos Economopoulos 	wmb();
15998892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
16008892ea20SAggelos Economopoulos 	srcp = src;
16018892ea20SAggelos Economopoulos 
16028892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
16035ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
16048892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
16058892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
16068892ea20SAggelos Economopoulos 			srcp += 2;
16078892ea20SAggelos Economopoulos 			dstp += 2;
16088892ea20SAggelos Economopoulos 		}
16098892ea20SAggelos Economopoulos 	} else {
16105ca32f31SSepherosa Ziehau 		/*
16115ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
16125ca32f31SSepherosa Ziehau 		 * that it is submitted below
16135ca32f31SSepherosa Ziehau 		 */
16148892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16158892ea20SAggelos Economopoulos 		i = 0;
16168892ea20SAggelos Economopoulos 	}
16178892ea20SAggelos Economopoulos 	if (i < cnt) {
16185ca32f31SSepherosa Ziehau 		/* Submit the first request */
16198892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16208892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16218892ea20SAggelos Economopoulos 	}
16228892ea20SAggelos Economopoulos 
16235ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16248892ea20SAggelos Economopoulos 	src->flags = last_flags;
16258892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16268892ea20SAggelos Economopoulos 	src_ints+=3;
16278892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16288892ea20SAggelos Economopoulos 	dst_ints+=3;
16298892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16308892ea20SAggelos Economopoulos 	tx->req += cnt;
16318892ea20SAggelos Economopoulos 	wmb();
16328892ea20SAggelos Economopoulos }
16338892ea20SAggelos Economopoulos 
163489d55360SSepherosa Ziehau static int
163589d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
163689d55360SSepherosa Ziehau {
163789d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
163889d55360SSepherosa Ziehau 	struct mbuf *m;
163989d55360SSepherosa Ziehau 
164089d55360SSepherosa Ziehau 	m = *mp;
164189d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
164289d55360SSepherosa Ziehau 
164389d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
164489d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
164589d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
164689d55360SSepherosa Ziehau 
164789d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
164889d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
164989d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
165089d55360SSepherosa Ziehau 
165189d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
165289d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
165389d55360SSepherosa Ziehau 		if (m == NULL) {
165489d55360SSepherosa Ziehau 			*mp = NULL;
165589d55360SSepherosa Ziehau 			return ENOBUFS;
165689d55360SSepherosa Ziehau 		}
165789d55360SSepherosa Ziehau 		*mp = m;
165889d55360SSepherosa Ziehau 	}
165989d55360SSepherosa Ziehau 	return 0;
166089d55360SSepherosa Ziehau }
16618892ea20SAggelos Economopoulos 
1662ca8ca004SSepherosa Ziehau static int
1663fb39a573SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mxge_buffer_state *info_map,
166448d12a0bSSepherosa Ziehau     struct mbuf *m, int busdma_seg_cnt)
16658892ea20SAggelos Economopoulos {
16668892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16678892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16688892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16698892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16708892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16718892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16728892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
1673fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_last;
167448d12a0bSSepherosa Ziehau 	bus_dmamap_t map = info_map->map;
16758892ea20SAggelos Economopoulos 
16768892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16778892ea20SAggelos Economopoulos 
16785ca32f31SSepherosa Ziehau 	/*
16795ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16805ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16818892ea20SAggelos Economopoulos 	 */
168289d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
168389d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16848892ea20SAggelos Economopoulos 
16855ca32f31SSepherosa Ziehau 	/*
16865ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16875ca32f31SSepherosa Ziehau 	 */
168889d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16898892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16908892ea20SAggelos Economopoulos 
16915ca32f31SSepherosa Ziehau 	/*
16925ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16935ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
16945ca32f31SSepherosa Ziehau 	 */
16958892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
16968892ea20SAggelos Economopoulos 
16978892ea20SAggelos Economopoulos 	req = tx->req_list;
16988892ea20SAggelos Economopoulos 	seg = tx->seg_list;
16998892ea20SAggelos Economopoulos 	cnt = 0;
17008892ea20SAggelos Economopoulos 	rdma_count = 0;
17015ca32f31SSepherosa Ziehau 
17025ca32f31SSepherosa Ziehau 	/*
17035ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
17045ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
17055ca32f31SSepherosa Ziehau 	 * this is equal to "count".
17068892ea20SAggelos Economopoulos 	 *
17075ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
17085ca32f31SSepherosa Ziehau 	 * segment cut.
17098892ea20SAggelos Economopoulos 	 *
17105ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
17115ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
17125ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
17135ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
17145ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
17155ca32f31SSepherosa Ziehau 	 * RDMA.
17165ca32f31SSepherosa Ziehau 	 *
17175ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
17185ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17195ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17208892ea20SAggelos Economopoulos 	 */
17218892ea20SAggelos Economopoulos 
17228892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17235ca32f31SSepherosa Ziehau 		/*
17245ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17255ca32f31SSepherosa Ziehau 		 */
17268892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17278892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17288892ea20SAggelos Economopoulos 		len = seg->ds_len;
17298892ea20SAggelos Economopoulos 
17308892ea20SAggelos Economopoulos 		while (len) {
17318892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17328892ea20SAggelos Economopoulos 			seglen = len;
17338892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17348892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17358892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17365ca32f31SSepherosa Ziehau 				/* Payload */
17378892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17388892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17398892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17408892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17415ca32f31SSepherosa Ziehau 				flags_next |=
17425ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17438892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17448892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17458892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17465ca32f31SSepherosa Ziehau 				/* Header ends */
17478892ea20SAggelos Economopoulos 				rdma_count = -1;
17488892ea20SAggelos Economopoulos 				cum_len_next = 0;
17498892ea20SAggelos Economopoulos 				seglen = -cum_len;
17508892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17518892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17528892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17538892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17548892ea20SAggelos Economopoulos 			}
17558892ea20SAggelos Economopoulos 
17568892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17578892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17588892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17598892ea20SAggelos Economopoulos 			req->pad = 0;
17608892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17618892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17628892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17635ca32f31SSepherosa Ziehau 			req->flags =
17645ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17658892ea20SAggelos Economopoulos 			low += seglen;
17668892ea20SAggelos Economopoulos 			len -= seglen;
17678892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17688892ea20SAggelos Economopoulos 			flags = flags_next;
17698892ea20SAggelos Economopoulos 			req++;
17708892ea20SAggelos Economopoulos 			cnt++;
17718892ea20SAggelos Economopoulos 			rdma_count++;
17728892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17738892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17748892ea20SAggelos Economopoulos 			else
17758892ea20SAggelos Economopoulos 				cksum_offset = 0;
17768892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17778892ea20SAggelos Economopoulos 				goto drop;
17788892ea20SAggelos Economopoulos 		}
17798892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17808892ea20SAggelos Economopoulos 		seg++;
17818892ea20SAggelos Economopoulos 	}
17828892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17838892ea20SAggelos Economopoulos 
17848892ea20SAggelos Economopoulos 	do {
17858892ea20SAggelos Economopoulos 		req--;
17868892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17878892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17888892ea20SAggelos Economopoulos 
178948d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
179048d12a0bSSepherosa Ziehau 
179148d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
179248d12a0bSSepherosa Ziehau 	info_last->map = map;
179348d12a0bSSepherosa Ziehau 	info_last->m = m;
179448d12a0bSSepherosa Ziehau 
17958892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
17960654769eSSepherosa Ziehau 
1797aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1798aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1799aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1800aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1801aca8f373SSepherosa Ziehau 		tx->activate++;
1802aca8f373SSepherosa Ziehau 		wmb();
1803aca8f373SSepherosa Ziehau 	}
1804ca8ca004SSepherosa Ziehau 	return 0;
18058892ea20SAggelos Economopoulos 
18068892ea20SAggelos Economopoulos drop:
18078892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
18088892ea20SAggelos Economopoulos 	m_freem(m);
1809ca8ca004SSepherosa Ziehau 	return ENOBUFS;
18108892ea20SAggelos Economopoulos }
18118892ea20SAggelos Economopoulos 
1812ca8ca004SSepherosa Ziehau static int
18135da1e9c3SSepherosa Ziehau mxge_encap(mxge_tx_ring_t *tx, struct mbuf *m, bus_addr_t zeropad)
18148892ea20SAggelos Economopoulos {
18158892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
18168892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
181748d12a0bSSepherosa Ziehau 	bus_dmamap_t map;
181889d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
18198892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
18208892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
1821fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_map, *info_last;
18228892ea20SAggelos Economopoulos 
182389d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1824ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1825ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1826ca8ca004SSepherosa Ziehau 			return err;
18278892ea20SAggelos Economopoulos 	}
182889d55360SSepherosa Ziehau 
18295ca32f31SSepherosa Ziehau 	/*
18305ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18315ca32f31SSepherosa Ziehau 	 */
183289d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
183348d12a0bSSepherosa Ziehau 	info_map = &tx->info[idx];
183448d12a0bSSepherosa Ziehau 	map = info_map->map;
183548d12a0bSSepherosa Ziehau 
183648d12a0bSSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, map, &m,
183789d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
183889d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
183989d55360SSepherosa Ziehau 		goto drop;
184048d12a0bSSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, map, BUS_DMASYNC_PREWRITE);
184189d55360SSepherosa Ziehau 
18425ca32f31SSepherosa Ziehau 	/*
18435ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18445ca32f31SSepherosa Ziehau 	 */
1845ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
184648d12a0bSSepherosa Ziehau 		return mxge_encap_tso(tx, info_map, m, cnt);
18478892ea20SAggelos Economopoulos 
18488892ea20SAggelos Economopoulos 	req = tx->req_list;
18498892ea20SAggelos Economopoulos 	cksum_offset = 0;
18508892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18518892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18528892ea20SAggelos Economopoulos 
18535ca32f31SSepherosa Ziehau 	/*
18545ca32f31SSepherosa Ziehau 	 * Checksum offloading
18555ca32f31SSepherosa Ziehau 	 */
185689d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
185789d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18588892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18598892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18608892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18618892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18628892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18638892ea20SAggelos Economopoulos 	} else {
18648892ea20SAggelos Economopoulos 		odd_flag = 0;
18658892ea20SAggelos Economopoulos 	}
18668892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18678892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18688892ea20SAggelos Economopoulos 
18695ca32f31SSepherosa Ziehau 	/*
18705ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18715ca32f31SSepherosa Ziehau 	 */
18728892ea20SAggelos Economopoulos 	cum_len = 0;
18738892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18748892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18758892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18765ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18775ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18788892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18798892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18808892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18818892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18828892ea20SAggelos Economopoulos 		else
18838892ea20SAggelos Economopoulos 			cksum_offset = 0;
18848892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18858892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18868892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18878892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18888892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18898892ea20SAggelos Economopoulos 		seg++;
18908892ea20SAggelos Economopoulos 		req++;
18918892ea20SAggelos Economopoulos 		req->flags = 0;
18928892ea20SAggelos Economopoulos 	}
18938892ea20SAggelos Economopoulos 	req--;
18945ca32f31SSepherosa Ziehau 
18955ca32f31SSepherosa Ziehau 	/*
18965ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
18975ca32f31SSepherosa Ziehau 	 */
18988892ea20SAggelos Economopoulos 	if (cum_len < 60) {
18998892ea20SAggelos Economopoulos 		req++;
19005da1e9c3SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(zeropad));
19015da1e9c3SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(zeropad));
19028892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
19038892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
19048892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
19058892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
19068892ea20SAggelos Economopoulos 		req->rdma_count = 1;
19078892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
19088892ea20SAggelos Economopoulos 		cnt++;
19098892ea20SAggelos Economopoulos 	}
19108892ea20SAggelos Economopoulos 
19118892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
19128892ea20SAggelos Economopoulos #if 0
19138892ea20SAggelos Economopoulos 	/* print what the firmware will see */
19148892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
19156c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
19168892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
19178892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19188892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19198892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19208892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19218892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19228892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19238892ea20SAggelos Economopoulos 	}
19246c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19258892ea20SAggelos Economopoulos #endif
192648d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
192748d12a0bSSepherosa Ziehau 
192848d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
192948d12a0bSSepherosa Ziehau 	info_last->map = map;
193048d12a0bSSepherosa Ziehau 	info_last->m = m;
193148d12a0bSSepherosa Ziehau 
19328892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19330654769eSSepherosa Ziehau 
1934aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1935aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1936aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1937aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1938aca8f373SSepherosa Ziehau 		tx->activate++;
1939aca8f373SSepherosa Ziehau 		wmb();
1940aca8f373SSepherosa Ziehau 	}
1941ca8ca004SSepherosa Ziehau 	return 0;
19428892ea20SAggelos Economopoulos 
19438892ea20SAggelos Economopoulos drop:
19448892ea20SAggelos Economopoulos 	m_freem(m);
1945ca8ca004SSepherosa Ziehau 	return err;
19468892ea20SAggelos Economopoulos }
19478892ea20SAggelos Economopoulos 
19488892ea20SAggelos Economopoulos static void
1949f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19508892ea20SAggelos Economopoulos {
19518892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
1952aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
19535da1e9c3SSepherosa Ziehau 	bus_addr_t zeropad;
1954ca8ca004SSepherosa Ziehau 	int encap = 0;
19558892ea20SAggelos Economopoulos 
1956aca8f373SSepherosa Ziehau 	KKASSERT(tx->ifsq == ifsq);
195726634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
1958795c96bbSSepherosa Ziehau 
1959795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1960795c96bbSSepherosa Ziehau 		return;
1961795c96bbSSepherosa Ziehau 
19625da1e9c3SSepherosa Ziehau 	zeropad = sc->zeropad_dma.dmem_busaddr;
1963795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1964795c96bbSSepherosa Ziehau 		struct mbuf *m;
1965ca8ca004SSepherosa Ziehau 		int error;
1966795c96bbSSepherosa Ziehau 
1967795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1968795c96bbSSepherosa Ziehau 		if (m == NULL)
1969ca8ca004SSepherosa Ziehau 			goto done;
1970795c96bbSSepherosa Ziehau 
1971795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
19725da1e9c3SSepherosa Ziehau 		error = mxge_encap(tx, m, zeropad);
1973ca8ca004SSepherosa Ziehau 		if (!error)
1974ca8ca004SSepherosa Ziehau 			encap = 1;
1975fed54363SSepherosa Ziehau 		else
1976fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1977795c96bbSSepherosa Ziehau 	}
1978795c96bbSSepherosa Ziehau 
1979795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1980795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1981ca8ca004SSepherosa Ziehau done:
1982ca8ca004SSepherosa Ziehau 	if (encap)
1983aca8f373SSepherosa Ziehau 		tx->watchdog.wd_timer = 5;
1984ca8ca004SSepherosa Ziehau }
1985ca8ca004SSepherosa Ziehau 
1986ca8ca004SSepherosa Ziehau static void
1987aca8f373SSepherosa Ziehau mxge_watchdog(struct ifaltq_subque *ifsq)
1988ca8ca004SSepherosa Ziehau {
1989aca8f373SSepherosa Ziehau 	struct ifnet *ifp = ifsq_get_ifp(ifsq);
1990ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1991ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1992aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
1993ca8ca004SSepherosa Ziehau 
199426634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
1995ca8ca004SSepherosa Ziehau 
1996ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
1997ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
1998ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
1999ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
2000ca8ca004SSepherosa Ziehau 		return;
2001ca8ca004SSepherosa Ziehau 	} else {
2002ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
2003ca8ca004SSepherosa Ziehau 		    "check link partner\n");
2004ca8ca004SSepherosa Ziehau 	}
2005ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
20068892ea20SAggelos Economopoulos }
20078892ea20SAggelos Economopoulos 
20088892ea20SAggelos Economopoulos /*
20092f47b54fSSepherosa Ziehau  * Copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
20108892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
20118892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's low
20128892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
20138892ea20SAggelos Economopoulos  * in a burst
20148892ea20SAggelos Economopoulos  */
2015ddbf91b7SSepherosa Ziehau static __inline void
20168892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
20178892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
20188892ea20SAggelos Economopoulos {
20198892ea20SAggelos Economopoulos 	uint32_t low;
20208892ea20SAggelos Economopoulos 
20218892ea20SAggelos Economopoulos 	low = src->addr_low;
20228892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
20238892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
20248892ea20SAggelos Economopoulos 	wmb();
20258892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
20268892ea20SAggelos Economopoulos 	wmb();
20278892ea20SAggelos Economopoulos 	src->addr_low = low;
20288892ea20SAggelos Economopoulos 	dst->addr_low = low;
20298892ea20SAggelos Economopoulos 	wmb();
20308892ea20SAggelos Economopoulos }
20318892ea20SAggelos Economopoulos 
20328892ea20SAggelos Economopoulos static int
20338ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20348ebf015eSSepherosa Ziehau     boolean_t init)
20358892ea20SAggelos Economopoulos {
20368892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20378892ea20SAggelos Economopoulos 	struct mbuf *m;
2038363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20398892ea20SAggelos Economopoulos 
2040b5523eacSSascha Wildner 	mflag = M_NOWAIT;
20418ebf015eSSepherosa Ziehau 	if (__predict_false(init))
2042b5523eacSSascha Wildner 		mflag = M_WAITOK;
20438ebf015eSSepherosa Ziehau 
20448ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20458892ea20SAggelos Economopoulos 	if (m == NULL) {
20468892ea20SAggelos Economopoulos 		err = ENOBUFS;
20478ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20488ebf015eSSepherosa Ziehau 			/*
20498ebf015eSSepherosa Ziehau 			 * During initialization, there
20508ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20518ebf015eSSepherosa Ziehau 			 */
20528ebf015eSSepherosa Ziehau 			return err;
20538ebf015eSSepherosa Ziehau 		}
20548892ea20SAggelos Economopoulos 		goto done;
20558892ea20SAggelos Economopoulos 	}
20562823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20578ebf015eSSepherosa Ziehau 
20587d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20597d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20608892ea20SAggelos Economopoulos 	if (err != 0) {
20618ebf015eSSepherosa Ziehau 		m_freem(m);
20628ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20638ebf015eSSepherosa Ziehau 			/*
20648ebf015eSSepherosa Ziehau 			 * During initialization, there
20658ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20668ebf015eSSepherosa Ziehau 			 */
20678ebf015eSSepherosa Ziehau 			return err;
20688ebf015eSSepherosa Ziehau 		}
20698892ea20SAggelos Economopoulos 		goto done;
20708892ea20SAggelos Economopoulos 	}
20718ebf015eSSepherosa Ziehau 
20728892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20738ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20748ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20758892ea20SAggelos Economopoulos 
20768892ea20SAggelos Economopoulos done:
20778892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20788892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20798892ea20SAggelos Economopoulos 	return err;
20808892ea20SAggelos Economopoulos }
20818892ea20SAggelos Economopoulos 
20828892ea20SAggelos Economopoulos static int
2083363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2084363b44f8SSepherosa Ziehau     boolean_t init)
20858892ea20SAggelos Economopoulos {
2086b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20878892ea20SAggelos Economopoulos 	struct mbuf *m;
2088363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2089363b44f8SSepherosa Ziehau 
2090b5523eacSSascha Wildner 	mflag = M_NOWAIT;
2091363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2092b5523eacSSascha Wildner 		mflag = M_WAITOK;
20938892ea20SAggelos Economopoulos 
20948892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2095363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2096b9a8961fSSepherosa Ziehau 	else
2097363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
20988892ea20SAggelos Economopoulos 	if (m == NULL) {
20998892ea20SAggelos Economopoulos 		err = ENOBUFS;
2100363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2101363b44f8SSepherosa Ziehau 			/*
2102363b44f8SSepherosa Ziehau 			 * During initialization, there
2103363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2104363b44f8SSepherosa Ziehau 			 */
2105363b44f8SSepherosa Ziehau 			return err;
2106363b44f8SSepherosa Ziehau 		}
21078892ea20SAggelos Economopoulos 		goto done;
21088892ea20SAggelos Economopoulos 	}
21093ae50fbeSSepherosa Ziehau 	m->m_len = m->m_pkthdr.len = rx->cl_size;
2110b9a8961fSSepherosa Ziehau 
21117d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2112b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
21138892ea20SAggelos Economopoulos 	if (err != 0) {
2114363b44f8SSepherosa Ziehau 		m_freem(m);
2115363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2116363b44f8SSepherosa Ziehau 			/*
2117363b44f8SSepherosa Ziehau 			 * During initialization, there
2118363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2119363b44f8SSepherosa Ziehau 			 */
2120363b44f8SSepherosa Ziehau 			return err;
2121363b44f8SSepherosa Ziehau 		}
21228892ea20SAggelos Economopoulos 		goto done;
21238892ea20SAggelos Economopoulos 	}
2124b9a8961fSSepherosa Ziehau 
21258892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2126363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2127363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21288892ea20SAggelos Economopoulos 
21298892ea20SAggelos Economopoulos done:
2130b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2131b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21328892ea20SAggelos Economopoulos 	return err;
21338892ea20SAggelos Economopoulos }
21348892ea20SAggelos Economopoulos 
21358892ea20SAggelos Economopoulos /*
21368892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21378892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21388892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21398892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21408892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21418892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21428892ea20SAggelos Economopoulos  */
214352cf8dfcSSepherosa Ziehau static __inline uint16_t
21448892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21458892ea20SAggelos Economopoulos {
214652cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
214752cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21488892ea20SAggelos Economopoulos 	uint16_t c;
21498892ea20SAggelos Economopoulos 
215052cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21518892ea20SAggelos Economopoulos 
215252cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21538892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21548892ea20SAggelos Economopoulos 		return 1;
215552cf8dfcSSepherosa Ziehau 
215652cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
215752cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21588892ea20SAggelos Economopoulos 		return 1;
215952cf8dfcSSepherosa Ziehau 
21608892ea20SAggelos Economopoulos #ifdef INET
21618892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21628892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21638892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21648892ea20SAggelos Economopoulos #else
21658892ea20SAggelos Economopoulos 	c = 1;
21668892ea20SAggelos Economopoulos #endif
21678892ea20SAggelos Economopoulos 	c ^= 0xffff;
216852cf8dfcSSepherosa Ziehau 	return c;
21698892ea20SAggelos Economopoulos }
21708892ea20SAggelos Economopoulos 
21718892ea20SAggelos Economopoulos static void
21728892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21738892ea20SAggelos Economopoulos {
21748892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21758892ea20SAggelos Economopoulos 	uint32_t partial;
21768892ea20SAggelos Economopoulos 
21778892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21788892ea20SAggelos Economopoulos 
21798892ea20SAggelos Economopoulos 	/*
218052cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
218152cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21828892ea20SAggelos Economopoulos 	 * header.
21838892ea20SAggelos Economopoulos 	 */
21848892ea20SAggelos Economopoulos 
218552cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21868892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21878892ea20SAggelos Economopoulos 
218852cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
218952cf8dfcSSepherosa Ziehau 	*csum += ~partial;
219052cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
219152cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
219252cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
219352cf8dfcSSepherosa Ziehau 
219452cf8dfcSSepherosa Ziehau 	/*
219552cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
219652cf8dfcSSepherosa Ziehau 	 * later consumers expect this
219752cf8dfcSSepherosa Ziehau 	 */
21988892ea20SAggelos Economopoulos 	*csum = htons(*csum);
21998892ea20SAggelos Economopoulos 
22008892ea20SAggelos Economopoulos 	/* save the tag */
2201b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
22028892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
22038892ea20SAggelos Economopoulos 
22048892ea20SAggelos Economopoulos 	/*
22058892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
22068892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
22078892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
22088892ea20SAggelos Economopoulos 	 * type field is already in place.
22098892ea20SAggelos Economopoulos 	 */
2210b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
22118892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2212b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
22138892ea20SAggelos Economopoulos }
22148892ea20SAggelos Economopoulos 
22158892ea20SAggelos Economopoulos 
221652cf8dfcSSepherosa Ziehau static __inline void
22175da1e9c3SSepherosa Ziehau mxge_rx_done_big(struct ifnet *ifp, mxge_rx_ring_t *rx,
22185da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22198892ea20SAggelos Economopoulos {
22208892ea20SAggelos Economopoulos 	struct mbuf *m;
222152cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22228892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22238892ea20SAggelos Economopoulos 	int idx;
22248892ea20SAggelos Economopoulos 
22258892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2226b9a8961fSSepherosa Ziehau 	rx->cnt++;
222752cf8dfcSSepherosa Ziehau 
222852cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22298892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
223052cf8dfcSSepherosa Ziehau 
223152cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2232363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
223352cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2234d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22358892ea20SAggelos Economopoulos 		return;
22368892ea20SAggelos Economopoulos 	}
22378892ea20SAggelos Economopoulos 
223852cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22398892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22408892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22418892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22428892ea20SAggelos Economopoulos 
224352cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22448892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22458892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22468892ea20SAggelos Economopoulos 
224752cf8dfcSSepherosa Ziehau 	/*
224852cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
224952cf8dfcSSepherosa Ziehau 	 * aligned
225052cf8dfcSSepherosa Ziehau 	 */
22518892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22528892ea20SAggelos Economopoulos 
22538892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22548892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
225552cf8dfcSSepherosa Ziehau 
2256cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
225752cf8dfcSSepherosa Ziehau 
225852cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
225952cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22608892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
226152cf8dfcSSepherosa Ziehau 
226252cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
226389d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
226452cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
226589d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22668892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
226789d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
226889d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22698892ea20SAggelos Economopoulos 	}
227073029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
22718892ea20SAggelos Economopoulos }
22728892ea20SAggelos Economopoulos 
227352cf8dfcSSepherosa Ziehau static __inline void
22745da1e9c3SSepherosa Ziehau mxge_rx_done_small(struct ifnet *ifp, mxge_rx_ring_t *rx,
22755da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22768892ea20SAggelos Economopoulos {
227752cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22788892ea20SAggelos Economopoulos 	struct mbuf *m;
22798892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22808892ea20SAggelos Economopoulos 	int idx;
22818892ea20SAggelos Economopoulos 
22828892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22838892ea20SAggelos Economopoulos 	rx->cnt++;
228452cf8dfcSSepherosa Ziehau 
228552cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22868892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
228752cf8dfcSSepherosa Ziehau 
228852cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22898ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
229052cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2291d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22928892ea20SAggelos Economopoulos 		return;
22938892ea20SAggelos Economopoulos 	}
22948892ea20SAggelos Economopoulos 
229552cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22968892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22978892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22988892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22998892ea20SAggelos Economopoulos 
230052cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
23018892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
23028892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
23038892ea20SAggelos Economopoulos 
230452cf8dfcSSepherosa Ziehau 	/*
230552cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
230652cf8dfcSSepherosa Ziehau 	 * aligned
230752cf8dfcSSepherosa Ziehau 	 */
23088892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
23098892ea20SAggelos Economopoulos 
23108892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
23118892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
231252cf8dfcSSepherosa Ziehau 
2313cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
231452cf8dfcSSepherosa Ziehau 
231552cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
231652cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
23178892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
231852cf8dfcSSepherosa Ziehau 
231952cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
232089d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
232152cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
232289d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
23238892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
232489d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
232589d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
23268892ea20SAggelos Economopoulos 	}
232773029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
23288892ea20SAggelos Economopoulos }
23298892ea20SAggelos Economopoulos 
233052cf8dfcSSepherosa Ziehau static __inline void
23312276707eSSepherosa Ziehau mxge_clean_rx_done(struct ifnet *ifp, struct mxge_rx_data *rx_data, int cycle)
23328892ea20SAggelos Economopoulos {
23335da1e9c3SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &rx_data->rx_done;
23345da1e9c3SSepherosa Ziehau 
23352276707eSSepherosa Ziehau 	while (rx_done->entry[rx_done->idx].length != 0 && cycle != 0) {
233652cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
233752cf8dfcSSepherosa Ziehau 
23388892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23398892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
234052cf8dfcSSepherosa Ziehau 
23418892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
234252cf8dfcSSepherosa Ziehau 
234360f8c66dSSepherosa Ziehau 		if (length <= MXGE_RX_SMALL_BUFLEN) {
23445da1e9c3SSepherosa Ziehau 			mxge_rx_done_small(ifp, &rx_data->rx_small,
23455da1e9c3SSepherosa Ziehau 			    length, checksum);
23465da1e9c3SSepherosa Ziehau 		} else {
23475da1e9c3SSepherosa Ziehau 			mxge_rx_done_big(ifp, &rx_data->rx_big,
23485da1e9c3SSepherosa Ziehau 			    length, checksum);
23495da1e9c3SSepherosa Ziehau 		}
235052cf8dfcSSepherosa Ziehau 
2351a3f51d6bSSepherosa Ziehau 		rx_done->idx++;
2352a3f51d6bSSepherosa Ziehau 		rx_done->idx &= rx_done->mask;
23532276707eSSepherosa Ziehau 		--cycle;
23548892ea20SAggelos Economopoulos 	}
23558892ea20SAggelos Economopoulos }
23568892ea20SAggelos Economopoulos 
2357ddbf91b7SSepherosa Ziehau static __inline void
23585da1e9c3SSepherosa Ziehau mxge_tx_done(struct ifnet *ifp, mxge_tx_ring_t *tx, uint32_t mcp_idx)
23598892ea20SAggelos Economopoulos {
236026634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
236166e7a0e8SSepherosa Ziehau 
23628892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
236366e7a0e8SSepherosa Ziehau 		struct mbuf *m;
236466e7a0e8SSepherosa Ziehau 		int idx;
236566e7a0e8SSepherosa Ziehau 
23668892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23678892ea20SAggelos Economopoulos 		tx->done++;
236866e7a0e8SSepherosa Ziehau 
23698892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
237066e7a0e8SSepherosa Ziehau 		/*
237166e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
237266e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
237366e7a0e8SSepherosa Ziehau 		 */
23748892ea20SAggelos Economopoulos 		if (m != NULL) {
237548d12a0bSSepherosa Ziehau 			tx->pkt_done++;
2376cc9c62a4SSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
23778892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
237866e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23798892ea20SAggelos Economopoulos 			m_freem(m);
23808892ea20SAggelos Economopoulos 		}
23818892ea20SAggelos Economopoulos 	}
23828892ea20SAggelos Economopoulos 
238366e7a0e8SSepherosa Ziehau 	/*
238466e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
238566e7a0e8SSepherosa Ziehau 	 * its OK to send packets
238666e7a0e8SSepherosa Ziehau 	 */
238783c2b410SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 2) {
2388aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
2389aca8f373SSepherosa Ziehau 		if (tx->req == tx->done) {
2390aca8f373SSepherosa Ziehau 			/* Reset watchdog */
2391aca8f373SSepherosa Ziehau 			tx->watchdog.wd_timer = 0;
2392aca8f373SSepherosa Ziehau 		}
2393ca8ca004SSepherosa Ziehau 	}
239489d55360SSepherosa Ziehau 
2395aca8f373SSepherosa Ziehau 	if (!ifsq_is_empty(tx->ifsq))
2396aca8f373SSepherosa Ziehau 		ifsq_devstart(tx->ifsq);
2397adb0d705SSepherosa Ziehau 
2398adb0d705SSepherosa Ziehau 	if (tx->send_stop != NULL && tx->req == tx->done) {
2399adb0d705SSepherosa Ziehau 		/*
2400adb0d705SSepherosa Ziehau 		 * Let the NIC stop polling this queue, since there
2401adb0d705SSepherosa Ziehau 		 * are no more transmits pending
2402adb0d705SSepherosa Ziehau 		 */
2403adb0d705SSepherosa Ziehau 		*tx->send_stop = 1;
2404adb0d705SSepherosa Ziehau 		tx->queue_active = 0;
2405adb0d705SSepherosa Ziehau 		tx->deactivate++;
2406adb0d705SSepherosa Ziehau 		wmb();
2407adb0d705SSepherosa Ziehau 	}
24088892ea20SAggelos Economopoulos }
24098892ea20SAggelos Economopoulos 
241089d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24118892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24128892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24138892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2414166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 5),	"10GBASE-ER"},
24158892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2416166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 3),	"10GBASE-SW"},
2417166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 2),	"10GBASE-LW"},
2418166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 1),	"10GBASE-EW"},
2419166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 0),	"Reserved"}
24208892ea20SAggelos Economopoulos };
242189d55360SSepherosa Ziehau 
242289d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
242389d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
2424166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 7),	"Reserved"},
24258892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24268892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
242789d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
242889d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24298892ea20SAggelos Economopoulos };
24308892ea20SAggelos Economopoulos 
24318892ea20SAggelos Economopoulos static void
243289d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24338892ea20SAggelos Economopoulos {
243400f2de12SSepherosa Ziehau 	int fc_opt = 0;
243500f2de12SSepherosa Ziehau 
2436166c46afSSepherosa Ziehau 	if (media_type == IFM_NONE)
2437166c46afSSepherosa Ziehau 		return;
2438166c46afSSepherosa Ziehau 
243900f2de12SSepherosa Ziehau 	if (sc->pause)
244000f2de12SSepherosa Ziehau 		fc_opt = IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
244100f2de12SSepherosa Ziehau 
244200f2de12SSepherosa Ziehau 	ifmedia_add(&sc->media, MXGE_IFM | media_type, 0, NULL);
244300f2de12SSepherosa Ziehau 	ifmedia_set(&sc->media, MXGE_IFM | media_type | fc_opt);
244400f2de12SSepherosa Ziehau 
244589d55360SSepherosa Ziehau 	sc->current_media = media_type;
2446166c46afSSepherosa Ziehau }
2447166c46afSSepherosa Ziehau 
2448166c46afSSepherosa Ziehau static void
2449166c46afSSepherosa Ziehau mxge_media_unset(mxge_softc_t *sc)
2450166c46afSSepherosa Ziehau {
2451166c46afSSepherosa Ziehau 	ifmedia_removeall(&sc->media);
2452166c46afSSepherosa Ziehau 	sc->current_media = IFM_NONE;
24538892ea20SAggelos Economopoulos }
24548892ea20SAggelos Economopoulos 
24558892ea20SAggelos Economopoulos static void
245689d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24578892ea20SAggelos Economopoulos {
2458c7431c78SSepherosa Ziehau 	const char *ptr;
245989d55360SSepherosa Ziehau 	int i;
24608892ea20SAggelos Economopoulos 
2461166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
24628892ea20SAggelos Economopoulos 
24638892ea20SAggelos Economopoulos 	/*
24642f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24658892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24668892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24678892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24688892ea20SAggelos Economopoulos 	 */
24698892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24708892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2471af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
247289d55360SSepherosa Ziehau 		return;
24738892ea20SAggelos Economopoulos 	}
24748892ea20SAggelos Economopoulos 
24758892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
247689d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24778892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2478af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24798892ea20SAggelos Economopoulos 			return;
24808892ea20SAggelos Economopoulos 		}
24818892ea20SAggelos Economopoulos 	}
248289d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24838892ea20SAggelos Economopoulos 		/* -C is CX4 */
248489d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
248589d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
248689d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24878892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
248889d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2489af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24902f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
249189d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
249289d55360SSepherosa Ziehau 		/* -R is XFP */
249389d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
2494166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
249589d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
249689d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
249789d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
2498166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
249989d55360SSepherosa Ziehau 	} else {
2500166c46afSSepherosa Ziehau 		sc->connector = MXGE_UNK;
2501af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
250289d55360SSepherosa Ziehau 	}
25038892ea20SAggelos Economopoulos }
25048892ea20SAggelos Economopoulos 
250589d55360SSepherosa Ziehau /*
250689d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
250789d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
250889d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
250989d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
251089d55360SSepherosa Ziehau  * than in the interrupt handler itself.
251189d55360SSepherosa Ziehau  */
251289d55360SSepherosa Ziehau static void
251389d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
251489d55360SSepherosa Ziehau {
251589d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
25167cc92483SSepherosa Ziehau 	const char *cage_type;
251789d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
251889d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
251989d55360SSepherosa Ziehau 	uint32_t byte;
252089d55360SSepherosa Ziehau 
252189d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
252289d55360SSepherosa Ziehau 
252389d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
25248892ea20SAggelos Economopoulos 		/* -R is XFP */
25258892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
2526b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_xfp_media_types);
25278892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25288892ea20SAggelos Economopoulos 		cage_type = "XFP";
252989d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25308892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25318892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
2532b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_sfp_media_types);
25338892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25348892ea20SAggelos Economopoulos 		byte = 3;
253589d55360SSepherosa Ziehau 	} else {
253689d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25378892ea20SAggelos Economopoulos 		return;
25388892ea20SAggelos Economopoulos 	}
25398892ea20SAggelos Economopoulos 
25408892ea20SAggelos Economopoulos 	/*
25418892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25428892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25438892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25448892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25458892ea20SAggelos Economopoulos 	 * a millisecond
25468892ea20SAggelos Economopoulos 	 */
25478892ea20SAggelos Economopoulos 
2548*57e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
25498892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25508892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25518892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
2552166c46afSSepherosa Ziehau 	if (err != MXGEFW_CMD_OK) {
25537cc92483SSepherosa Ziehau 		if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2554af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "failed to read XFP\n");
2555166c46afSSepherosa Ziehau 		else if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2556af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
2557166c46afSSepherosa Ziehau 		else
2558166c46afSSepherosa Ziehau 			if_printf(sc->ifp, "I2C read failed, err: %d", err);
2559166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25608892ea20SAggelos Economopoulos 		return;
2561166c46afSSepherosa Ziehau 	}
25628892ea20SAggelos Economopoulos 
25637cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25648892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25658892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25667cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25678892ea20SAggelos Economopoulos 		DELAY(1000);
25688892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25698892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25708892ea20SAggelos Economopoulos 	}
25718892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2572af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25738892ea20SAggelos Economopoulos 		    cage_type, err, ms);
2574166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25758892ea20SAggelos Economopoulos 		return;
25768892ea20SAggelos Economopoulos 	}
25778892ea20SAggelos Economopoulos 
25788892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25797cc92483SSepherosa Ziehau 		if (bootverbose) {
2580af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25818892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25827cc92483SSepherosa Ziehau 		}
258389d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
2584166c46afSSepherosa Ziehau 			mxge_media_unset(sc);
258589d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
258689d55360SSepherosa Ziehau 		}
25878892ea20SAggelos Economopoulos 		return;
25888892ea20SAggelos Economopoulos 	}
25898892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25908892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25917cc92483SSepherosa Ziehau 			if (bootverbose) {
2592af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25938892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
25947cc92483SSepherosa Ziehau 			}
25958892ea20SAggelos Economopoulos 
259689d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
2597166c46afSSepherosa Ziehau 				mxge_media_unset(sc);
259889d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
259989d55360SSepherosa Ziehau 			}
26008892ea20SAggelos Economopoulos 			return;
26018892ea20SAggelos Economopoulos 		}
26028892ea20SAggelos Economopoulos 	}
2603166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
26047cc92483SSepherosa Ziehau 	if (bootverbose) {
2605af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
26067cc92483SSepherosa Ziehau 		    cmd.data0);
26077cc92483SSepherosa Ziehau 	}
26088892ea20SAggelos Economopoulos }
26098892ea20SAggelos Economopoulos 
26108892ea20SAggelos Economopoulos static void
2611cf5afd69SSepherosa Ziehau mxge_intr_status(struct mxge_softc *sc, const mcp_irq_data_t *stats)
26128892ea20SAggelos Economopoulos {
26138892ea20SAggelos Economopoulos 	if (sc->link_state != stats->link_up) {
26148892ea20SAggelos Economopoulos 		sc->link_state = stats->link_up;
26158892ea20SAggelos Economopoulos 		if (sc->link_state) {
261673a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_UP;
261773a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26187cc92483SSepherosa Ziehau 			if (bootverbose)
2619cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link up\n");
26208892ea20SAggelos Economopoulos 		} else {
262173a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
262273a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26237cc92483SSepherosa Ziehau 			if (bootverbose)
2624cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link down\n");
26258892ea20SAggelos Economopoulos 		}
26268892ea20SAggelos Economopoulos 		sc->need_media_probe = 1;
26278892ea20SAggelos Economopoulos 	}
2628cf5afd69SSepherosa Ziehau 
2629cf5afd69SSepherosa Ziehau 	if (sc->rdma_tags_available != be32toh(stats->rdma_tags_available)) {
2630cf5afd69SSepherosa Ziehau 		sc->rdma_tags_available = be32toh(stats->rdma_tags_available);
2631cf5afd69SSepherosa Ziehau 		if_printf(sc->ifp, "RDMA timed out! %d tags left\n",
2632cf5afd69SSepherosa Ziehau 		    sc->rdma_tags_available);
26338892ea20SAggelos Economopoulos 	}
26348892ea20SAggelos Economopoulos 
26358892ea20SAggelos Economopoulos 	if (stats->link_down) {
26368892ea20SAggelos Economopoulos 		sc->down_cnt += stats->link_down;
26378892ea20SAggelos Economopoulos 		sc->link_state = 0;
2638f0115d64SAggelos Economopoulos 		sc->ifp->if_link_state = LINK_STATE_DOWN;
2639f0115d64SAggelos Economopoulos 		if_link_state_change(sc->ifp);
26408892ea20SAggelos Economopoulos 	}
26418892ea20SAggelos Economopoulos }
26428892ea20SAggelos Economopoulos 
2643cf5afd69SSepherosa Ziehau static void
264426634ef8SSepherosa Ziehau mxge_serialize_skipmain(struct mxge_softc *sc)
264526634ef8SSepherosa Ziehau {
264626634ef8SSepherosa Ziehau 	lwkt_serialize_array_enter(sc->serializes, sc->nserialize, 1);
264726634ef8SSepherosa Ziehau }
264826634ef8SSepherosa Ziehau 
264926634ef8SSepherosa Ziehau static void
265026634ef8SSepherosa Ziehau mxge_deserialize_skipmain(struct mxge_softc *sc)
265126634ef8SSepherosa Ziehau {
265226634ef8SSepherosa Ziehau 	lwkt_serialize_array_exit(sc->serializes, sc->nserialize, 1);
265326634ef8SSepherosa Ziehau }
265426634ef8SSepherosa Ziehau 
265526634ef8SSepherosa Ziehau static void
2656cf5afd69SSepherosa Ziehau mxge_legacy(void *arg)
2657cf5afd69SSepherosa Ziehau {
2658cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2659cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2660cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2661cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26629a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2663cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2664cf5afd69SSepherosa Ziehau 	uint8_t valid;
2665cf5afd69SSepherosa Ziehau 
266626634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
266726634ef8SSepherosa Ziehau 
2668cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2669cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2670cf5afd69SSepherosa Ziehau 		return;
2671cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2672cf5afd69SSepherosa Ziehau 
2673cf5afd69SSepherosa Ziehau 	/* Lower legacy IRQ */
2674cf5afd69SSepherosa Ziehau 	*sc->irq_deassert = 0;
2675cf5afd69SSepherosa Ziehau 	if (!mxge_deassert_wait) {
2676cf5afd69SSepherosa Ziehau 		/* Don't wait for conf. that irq is low */
2677cf5afd69SSepherosa Ziehau 		stats->valid = 0;
2678cf5afd69SSepherosa Ziehau 	}
2679cf5afd69SSepherosa Ziehau 
268026634ef8SSepherosa Ziehau 	mxge_serialize_skipmain(sc);
268126634ef8SSepherosa Ziehau 
2682cf5afd69SSepherosa Ziehau 	/*
2683cf5afd69SSepherosa Ziehau 	 * Loop while waiting for legacy irq deassertion
2684cf5afd69SSepherosa Ziehau 	 * XXX do we really want to loop?
2685cf5afd69SSepherosa Ziehau 	 */
2686cf5afd69SSepherosa Ziehau 	do {
2687cf5afd69SSepherosa Ziehau 		/* Check for transmit completes and receives */
2688cf5afd69SSepherosa Ziehau 		send_done_count = be32toh(stats->send_done_count);
2689cf5afd69SSepherosa Ziehau 		while ((send_done_count != tx->pkt_done) ||
2690cf5afd69SSepherosa Ziehau 		       (rx_done->entry[rx_done->idx].length != 0)) {
26915da1e9c3SSepherosa Ziehau 			if (send_done_count != tx->pkt_done) {
26925da1e9c3SSepherosa Ziehau 				mxge_tx_done(&sc->arpcom.ac_if, tx,
26935da1e9c3SSepherosa Ziehau 				    (int)send_done_count);
26945da1e9c3SSepherosa Ziehau 			}
26952276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2696cf5afd69SSepherosa Ziehau 			send_done_count = be32toh(stats->send_done_count);
2697cf5afd69SSepherosa Ziehau 		}
2698cf5afd69SSepherosa Ziehau 		if (mxge_deassert_wait)
2699cf5afd69SSepherosa Ziehau 			wmb();
2700cf5afd69SSepherosa Ziehau 	} while (*((volatile uint8_t *)&stats->valid));
2701cf5afd69SSepherosa Ziehau 
270226634ef8SSepherosa Ziehau 	mxge_deserialize_skipmain(sc);
270326634ef8SSepherosa Ziehau 
2704cf5afd69SSepherosa Ziehau 	/* Fw link & error stats meaningful only on the first slice */
2705cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2706cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2707cf5afd69SSepherosa Ziehau 
2708cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
2709cf5afd69SSepherosa Ziehau 	if (valid & 0x1)
2710cf5afd69SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2711cf5afd69SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2712cf5afd69SSepherosa Ziehau }
2713cf5afd69SSepherosa Ziehau 
2714cf5afd69SSepherosa Ziehau static void
2715cf5afd69SSepherosa Ziehau mxge_msi(void *arg)
2716cf5afd69SSepherosa Ziehau {
2717cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2718cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2719cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2720cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
27219a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2722cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2723cf5afd69SSepherosa Ziehau 	uint8_t valid;
27242276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
27252276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
27262276707eSSepherosa Ziehau #else
27272276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
27282276707eSSepherosa Ziehau #endif
2729cf5afd69SSepherosa Ziehau 
273026634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
273126634ef8SSepherosa Ziehau 
2732cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
273376b52164SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2734cf5afd69SSepherosa Ziehau 		return;
2735cf5afd69SSepherosa Ziehau 
2736cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2737cf5afd69SSepherosa Ziehau 	stats->valid = 0;
2738cf5afd69SSepherosa Ziehau 
27392276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27402276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27412276707eSSepherosa Ziehau 		polling = TRUE;
27422276707eSSepherosa Ziehau #endif
27432276707eSSepherosa Ziehau 
27442276707eSSepherosa Ziehau 	if (!polling) {
2745cf5afd69SSepherosa Ziehau 		/* Check for receives */
274626634ef8SSepherosa Ziehau 		lwkt_serialize_enter(&ss->rx_data.rx_serialize);
2747cf5afd69SSepherosa Ziehau 		if (rx_done->entry[rx_done->idx].length != 0)
27482276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
274926634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&ss->rx_data.rx_serialize);
27502276707eSSepherosa Ziehau 	}
2751cf5afd69SSepherosa Ziehau 
275245bc9b9dSSepherosa Ziehau 	/*
275345bc9b9dSSepherosa Ziehau 	 * Check for transmit completes
275445bc9b9dSSepherosa Ziehau 	 *
275545bc9b9dSSepherosa Ziehau 	 * NOTE:
275645bc9b9dSSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
275745bc9b9dSSepherosa Ziehau 	 * which is called only in interrupt handler, the
275845bc9b9dSSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
275945bc9b9dSSepherosa Ziehau 	 */
2760cf5afd69SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
276145bc9b9dSSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
276245bc9b9dSSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
27635da1e9c3SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
276426634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
276545bc9b9dSSepherosa Ziehau 	}
2766cf5afd69SSepherosa Ziehau 
2767cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2768cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2769cf5afd69SSepherosa Ziehau 
2770cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
27712276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
27728892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
27738892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
27748892ea20SAggelos Economopoulos }
27758892ea20SAggelos Economopoulos 
27768892ea20SAggelos Economopoulos static void
2777e6c7b753SSepherosa Ziehau mxge_msix_rx(void *arg)
2778e6c7b753SSepherosa Ziehau {
2779e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2780e6c7b753SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2781e6c7b753SSepherosa Ziehau 
27822276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27832276707eSSepherosa Ziehau 	if (ss->sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27842276707eSSepherosa Ziehau 		return;
27852276707eSSepherosa Ziehau #endif
27862276707eSSepherosa Ziehau 
2787e6c7b753SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2788e6c7b753SSepherosa Ziehau 
2789e6c7b753SSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0)
27902276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, -1);
2791e6c7b753SSepherosa Ziehau 
2792e6c7b753SSepherosa Ziehau 	*ss->irq_claim = be32toh(3);
2793e6c7b753SSepherosa Ziehau }
2794e6c7b753SSepherosa Ziehau 
2795e6c7b753SSepherosa Ziehau static void
2796aca8f373SSepherosa Ziehau mxge_msix_rxtx(void *arg)
2797aca8f373SSepherosa Ziehau {
2798aca8f373SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2799aca8f373SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2800aca8f373SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2801aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
2802aca8f373SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2803aca8f373SSepherosa Ziehau 	uint32_t send_done_count;
2804aca8f373SSepherosa Ziehau 	uint8_t valid;
28052276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
28062276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
28072276707eSSepherosa Ziehau #else
28082276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
28092276707eSSepherosa Ziehau #endif
2810aca8f373SSepherosa Ziehau 
2811aca8f373SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2812aca8f373SSepherosa Ziehau 
2813aca8f373SSepherosa Ziehau 	/* Make sure the DMA has finished */
2814aca8f373SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2815aca8f373SSepherosa Ziehau 		return;
2816aca8f373SSepherosa Ziehau 
2817aca8f373SSepherosa Ziehau 	valid = stats->valid;
2818aca8f373SSepherosa Ziehau 	stats->valid = 0;
2819aca8f373SSepherosa Ziehau 
28202276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
28212276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
28222276707eSSepherosa Ziehau 		polling = TRUE;
28232276707eSSepherosa Ziehau #endif
28242276707eSSepherosa Ziehau 
2825aca8f373SSepherosa Ziehau 	/* Check for receives */
28262276707eSSepherosa Ziehau 	if (!polling && rx_done->entry[rx_done->idx].length != 0)
28272276707eSSepherosa Ziehau 		mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2828aca8f373SSepherosa Ziehau 
2829aca8f373SSepherosa Ziehau 	/*
2830aca8f373SSepherosa Ziehau 	 * Check for transmit completes
2831aca8f373SSepherosa Ziehau 	 *
2832aca8f373SSepherosa Ziehau 	 * NOTE:
2833aca8f373SSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
2834aca8f373SSepherosa Ziehau 	 * which is called only in interrupt handler, the
2835aca8f373SSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
2836aca8f373SSepherosa Ziehau 	 */
2837aca8f373SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
2838aca8f373SSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
2839aca8f373SSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
2840aca8f373SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
2841aca8f373SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
2842aca8f373SSepherosa Ziehau 	}
2843aca8f373SSepherosa Ziehau 
2844aca8f373SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
28452276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
2846aca8f373SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2847aca8f373SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2848aca8f373SSepherosa Ziehau }
2849aca8f373SSepherosa Ziehau 
2850aca8f373SSepherosa Ziehau static void
28518892ea20SAggelos Economopoulos mxge_init(void *arg)
28528892ea20SAggelos Economopoulos {
285389d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
285489d55360SSepherosa Ziehau 
285526634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(sc->ifp);
285689d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
285789d55360SSepherosa Ziehau 		mxge_open(sc);
28588892ea20SAggelos Economopoulos }
28598892ea20SAggelos Economopoulos 
28608892ea20SAggelos Economopoulos static void
28618892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
28628892ea20SAggelos Economopoulos {
28638892ea20SAggelos Economopoulos 	int i;
28648892ea20SAggelos Economopoulos 
28659a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
28669a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.info[i].m == NULL)
28678892ea20SAggelos Economopoulos 			continue;
28689a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_big.dmat,
28699a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map);
28709a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_big.info[i].m);
28719a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info[i].m = NULL;
28728892ea20SAggelos Economopoulos 	}
28738892ea20SAggelos Economopoulos 
28749a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
28759a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.info[i].m == NULL)
28768892ea20SAggelos Economopoulos 			continue;
28779a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_small.dmat,
28789a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map);
28799a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_small.info[i].m);
28809a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info[i].m = NULL;
28818892ea20SAggelos Economopoulos 	}
28828892ea20SAggelos Economopoulos 
28834e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
28848892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
28858892ea20SAggelos Economopoulos 		return;
28868892ea20SAggelos Economopoulos 
28878892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
28888892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
28898892ea20SAggelos Economopoulos 			continue;
28904e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
28918892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
28928892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
28938892ea20SAggelos Economopoulos 	}
28948892ea20SAggelos Economopoulos }
28958892ea20SAggelos Economopoulos 
28968892ea20SAggelos Economopoulos static void
28978892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
28988892ea20SAggelos Economopoulos {
28998892ea20SAggelos Economopoulos 	int slice;
29008892ea20SAggelos Economopoulos 
29018892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29028892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
29038892ea20SAggelos Economopoulos }
29048892ea20SAggelos Economopoulos 
29058892ea20SAggelos Economopoulos static void
29068892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
29078892ea20SAggelos Economopoulos {
29088892ea20SAggelos Economopoulos 	int i;
29098892ea20SAggelos Economopoulos 
29109a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_done.entry != NULL) {
2911414caf0dSSepherosa Ziehau 		mxge_dma_free(&ss->rx_done_dma);
29129a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = NULL;
2913798c3369SSepherosa Ziehau 	}
29148892ea20SAggelos Economopoulos 
291511868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
291611868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
291711868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2918798c3369SSepherosa Ziehau 	}
29198892ea20SAggelos Economopoulos 
2920798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2921d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
29228892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2923798c3369SSepherosa Ziehau 	}
29248892ea20SAggelos Economopoulos 
29259a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.shadow != NULL) {
29269a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.shadow, M_DEVBUF);
29279a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.shadow = NULL;
2928798c3369SSepherosa Ziehau 	}
29298892ea20SAggelos Economopoulos 
29309a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.shadow != NULL) {
29319a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.shadow, M_DEVBUF);
29329a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow = NULL;
2933798c3369SSepherosa Ziehau 	}
29348892ea20SAggelos Economopoulos 
29358892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
29368892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
29378892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
29388892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
29398892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
29408892ea20SAggelos Economopoulos 			}
29418892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
29428892ea20SAggelos Economopoulos 		}
2943d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
29448892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2945798c3369SSepherosa Ziehau 	}
29468892ea20SAggelos Economopoulos 
29479a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.info != NULL) {
29489a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.dmat != NULL) {
29499a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
29509a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29519a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[i].map);
29528892ea20SAggelos Economopoulos 			}
29539a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29549a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
29559a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29568892ea20SAggelos Economopoulos 		}
29579a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.info, M_DEVBUF);
29589a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info = NULL;
2959798c3369SSepherosa Ziehau 	}
29608892ea20SAggelos Economopoulos 
29619a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.info != NULL) {
29629a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.dmat != NULL) {
29639a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
29649a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29659a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[i].map);
29668892ea20SAggelos Economopoulos 			}
29679a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29689a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
29699a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29708892ea20SAggelos Economopoulos 		}
29719a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.info, M_DEVBUF);
29729a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info = NULL;
29738892ea20SAggelos Economopoulos 	}
2974798c3369SSepherosa Ziehau }
29758892ea20SAggelos Economopoulos 
29768892ea20SAggelos Economopoulos static void
29778892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
29788892ea20SAggelos Economopoulos {
29798892ea20SAggelos Economopoulos 	int slice;
29808892ea20SAggelos Economopoulos 
2981798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2982798c3369SSepherosa Ziehau 		return;
2983798c3369SSepherosa Ziehau 
29848892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29858892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
29868892ea20SAggelos Economopoulos }
29878892ea20SAggelos Economopoulos 
29888892ea20SAggelos Economopoulos static int
29898892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
29908892ea20SAggelos Economopoulos     int tx_ring_entries)
29918892ea20SAggelos Economopoulos {
29928892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
29938892ea20SAggelos Economopoulos 	size_t bytes;
29948892ea20SAggelos Economopoulos 	int err, i;
29958892ea20SAggelos Economopoulos 
29967cc92483SSepherosa Ziehau 	/*
29977cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
29987cc92483SSepherosa Ziehau 	 */
29998892ea20SAggelos Economopoulos 
30009a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.mask = ss->rx_data.rx_big.mask =
30019a4ae890SSepherosa Ziehau 	    rx_ring_entries - 1;
30029a4ae890SSepherosa Ziehau 	ss->rx_data.rx_done.mask = (2 * rx_ring_entries) - 1;
30038892ea20SAggelos Economopoulos 
30047cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
30059a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.shadow);
30069a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30078892ea20SAggelos Economopoulos 
30089a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.shadow);
30099a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30108892ea20SAggelos Economopoulos 
30117cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
30129a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.info);
30139a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30148892ea20SAggelos Economopoulos 
30159a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.info);
30169a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30178892ea20SAggelos Economopoulos 
30187cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
30198892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30208892ea20SAggelos Economopoulos 				 1,			/* alignment */
30218892ea20SAggelos Economopoulos 				 4096,			/* boundary */
30228892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30238892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30248892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
30258892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
30268892ea20SAggelos Economopoulos 				 1,			/* num segs */
30278892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
30287cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
30297cc92483SSepherosa Ziehau 				 			/* flags */
30309a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_small.dmat); /* tag */
30318892ea20SAggelos Economopoulos 	if (err != 0) {
30328892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
30338892ea20SAggelos Economopoulos 		    err);
30343598cc14SSascha Wildner 		return err;
30358892ea20SAggelos Economopoulos 	}
30368892ea20SAggelos Economopoulos 
30379a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_small.dmat, BUS_DMA_WAITOK,
30389a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_small.extra_map);
3039798c3369SSepherosa Ziehau 	if (err != 0) {
3040798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
30419a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
30429a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.dmat = NULL;
3043798c3369SSepherosa Ziehau 		return err;
3044798c3369SSepherosa Ziehau 	}
30459a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
30469a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_small.dmat,
30479a4ae890SSepherosa Ziehau 		    BUS_DMA_WAITOK, &ss->rx_data.rx_small.info[i].map);
3048798c3369SSepherosa Ziehau 		if (err != 0) {
3049798c3369SSepherosa Ziehau 			int j;
3050798c3369SSepherosa Ziehau 
3051798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
3052798c3369SSepherosa Ziehau 
3053798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
30549a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
30559a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[j].map);
3056798c3369SSepherosa Ziehau 			}
30579a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
30589a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
30599a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
30609a4ae890SSepherosa Ziehau 			ss->rx_data.rx_small.dmat = NULL;
3061798c3369SSepherosa Ziehau 			return err;
3062798c3369SSepherosa Ziehau 		}
3063798c3369SSepherosa Ziehau 	}
3064798c3369SSepherosa Ziehau 
30658892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30668892ea20SAggelos Economopoulos 				 1,			/* alignment */
30678892ea20SAggelos Economopoulos 				 4096,			/* boundary */
30688892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30698892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30708892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
3071b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
3072b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
30738892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
30747cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
30757cc92483SSepherosa Ziehau 				 			/* flags */
30769a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_big.dmat); /* tag */
30778892ea20SAggelos Economopoulos 	if (err != 0) {
30788892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
30798892ea20SAggelos Economopoulos 		    err);
30803598cc14SSascha Wildner 		return err;
30818892ea20SAggelos Economopoulos 	}
30827cc92483SSepherosa Ziehau 
30839a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30849a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_big.extra_map);
30858892ea20SAggelos Economopoulos 	if (err != 0) {
30867cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
30879a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
30889a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.dmat = NULL;
30893598cc14SSascha Wildner 		return err;
30908892ea20SAggelos Economopoulos 	}
30919a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
30929a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30939a4ae890SSepherosa Ziehau 		    &ss->rx_data.rx_big.info[i].map);
3094798c3369SSepherosa Ziehau 		if (err != 0) {
3095798c3369SSepherosa Ziehau 			int j;
3096798c3369SSepherosa Ziehau 
3097798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
3098798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
30999a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31009a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[j].map);
3101798c3369SSepherosa Ziehau 			}
31029a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31039a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
31049a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
31059a4ae890SSepherosa Ziehau 			ss->rx_data.rx_big.dmat = NULL;
3106798c3369SSepherosa Ziehau 			return err;
3107798c3369SSepherosa Ziehau 		}
3108798c3369SSepherosa Ziehau 	}
31098892ea20SAggelos Economopoulos 
31107cc92483SSepherosa Ziehau 	/*
31117cc92483SSepherosa Ziehau 	 * Now allocate TX resources
31127cc92483SSepherosa Ziehau 	 */
31138892ea20SAggelos Economopoulos 
31148892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
31158892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
31168892ea20SAggelos Economopoulos 
3117dfda108aSSepherosa Ziehau 	/*
3118dfda108aSSepherosa Ziehau 	 * Allocate the tx request copy block; MUST be at least 8 bytes
3119dfda108aSSepherosa Ziehau 	 * aligned
3120dfda108aSSepherosa Ziehau 	 */
312111868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
3122dfda108aSSepherosa Ziehau 	ss->tx.req_list = kmalloc_cachealign(__VM_CACHELINE_ALIGN(bytes),
3123dfda108aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
31248892ea20SAggelos Economopoulos 
31257cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
31268892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
31277cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
31288892ea20SAggelos Economopoulos 
31297cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
31308892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
3131d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31328892ea20SAggelos Economopoulos 
31337cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
31348892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
31358892ea20SAggelos Economopoulos 				 1,			/* alignment */
31368892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
31378892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
31388892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
31398892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
31407cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
31417cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
31427cc92483SSepherosa Ziehau 				 			/* maxsize */
31438892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
31448892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
31457cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
31467cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
31478892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
31488892ea20SAggelos Economopoulos 	if (err != 0) {
31497cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
31503598cc14SSascha Wildner 		return err;
31518892ea20SAggelos Economopoulos 	}
31528892ea20SAggelos Economopoulos 
31537cc92483SSepherosa Ziehau 	/*
31547cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
31557cc92483SSepherosa Ziehau 	 */
31568892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
31577cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
31587cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
31598892ea20SAggelos Economopoulos 		if (err != 0) {
3160798c3369SSepherosa Ziehau 			int j;
3161798c3369SSepherosa Ziehau 
31627cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
3163798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
3164798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
3165798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
3166798c3369SSepherosa Ziehau 			}
3167798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
3168798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
31693598cc14SSascha Wildner 			return err;
31708892ea20SAggelos Economopoulos 		}
31718892ea20SAggelos Economopoulos 	}
31728892ea20SAggelos Economopoulos 	return 0;
31738892ea20SAggelos Economopoulos }
31748892ea20SAggelos Economopoulos 
31758892ea20SAggelos Economopoulos static int
31768892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
31778892ea20SAggelos Economopoulos {
31788892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
31798892ea20SAggelos Economopoulos 	int tx_ring_size;
31808892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
31818892ea20SAggelos Economopoulos 	int err, slice;
31828892ea20SAggelos Economopoulos 
31837cc92483SSepherosa Ziehau 	/* Get ring sizes */
31848892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
31858892ea20SAggelos Economopoulos 	if (err != 0) {
31868892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3187798c3369SSepherosa Ziehau 		return err;
31888892ea20SAggelos Economopoulos 	}
31897cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
31908892ea20SAggelos Economopoulos 
31918892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
3192089301c2SSepherosa Ziehau 	rx_ring_entries = sc->rx_intr_slots / 2;
3193aca8f373SSepherosa Ziehau 
319418f86fd8SSepherosa Ziehau 	if (bootverbose) {
319518f86fd8SSepherosa Ziehau 		device_printf(sc->dev, "tx desc %d, rx desc %d\n",
319618f86fd8SSepherosa Ziehau 		    tx_ring_entries, rx_ring_entries);
319718f86fd8SSepherosa Ziehau 	}
319818f86fd8SSepherosa Ziehau 
319914929979SSepherosa Ziehau 	sc->ifp->if_nmbclusters = rx_ring_entries * sc->num_slices;
320014929979SSepherosa Ziehau 	sc->ifp->if_nmbjclusters = sc->ifp->if_nmbclusters;
320114929979SSepherosa Ziehau 
3202f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3203f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
3204aca8f373SSepherosa Ziehau 	ifq_set_subq_cnt(&sc->ifp->if_snd, sc->num_tx_rings);
3205aca8f373SSepherosa Ziehau 
3206aca8f373SSepherosa Ziehau 	if (sc->num_tx_rings > 1) {
3207aca8f373SSepherosa Ziehau 		sc->ifp->if_mapsubq = ifq_mapsubq_mask;
3208aca8f373SSepherosa Ziehau 		ifq_set_subq_mask(&sc->ifp->if_snd, sc->num_tx_rings - 1);
3209aca8f373SSepherosa Ziehau 	}
32108892ea20SAggelos Economopoulos 
32118892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
32128892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
32137cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3214798c3369SSepherosa Ziehau 		if (err != 0) {
3215798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3216798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3217798c3369SSepherosa Ziehau 			return err;
3218798c3369SSepherosa Ziehau 		}
32198892ea20SAggelos Economopoulos 	}
32208892ea20SAggelos Economopoulos 	return 0;
32218892ea20SAggelos Economopoulos }
32228892ea20SAggelos Economopoulos 
32238892ea20SAggelos Economopoulos static void
3224b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
32258892ea20SAggelos Economopoulos {
3226b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
32278892ea20SAggelos Economopoulos 
32288892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
32298892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3230b9a8961fSSepherosa Ziehau 	} else {
3231b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
32328892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
32338892ea20SAggelos Economopoulos 	}
32348892ea20SAggelos Economopoulos }
32358892ea20SAggelos Economopoulos 
32368892ea20SAggelos Economopoulos static int
3237b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
32388892ea20SAggelos Economopoulos {
32398892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32408892ea20SAggelos Economopoulos 	int err, i, slice;
32418892ea20SAggelos Economopoulos 
3242308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
32438892ea20SAggelos Economopoulos 
3244308781adSSepherosa Ziehau 	/*
3245308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3246308781adSSepherosa Ziehau 	 */
32478892ea20SAggelos Economopoulos 	err = 0;
3248aca8f373SSepherosa Ziehau 
3249*57e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
3250aca8f373SSepherosa Ziehau 	if (ss->sc->num_tx_rings == 1) {
3251aca8f373SSepherosa Ziehau 		if (slice == 0) {
3252aca8f373SSepherosa Ziehau 			cmd.data0 = slice;
3253aca8f373SSepherosa Ziehau 			err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET,
3254aca8f373SSepherosa Ziehau 			    &cmd);
3255aca8f373SSepherosa Ziehau 			ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3256aca8f373SSepherosa Ziehau 			    (ss->sc->sram + cmd.data0);
3257aca8f373SSepherosa Ziehau 			/* Leave send_go and send_stop as NULL */
3258aca8f373SSepherosa Ziehau 		}
3259aca8f373SSepherosa Ziehau 	} else {
3260aca8f373SSepherosa Ziehau 		cmd.data0 = slice;
3261aca8f373SSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3262aca8f373SSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3263aca8f373SSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
3264aca8f373SSepherosa Ziehau 		ss->tx.send_go = (volatile uint32_t *)
3265aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3266aca8f373SSepherosa Ziehau 		ss->tx.send_stop = (volatile uint32_t *)
3267aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3268aca8f373SSepherosa Ziehau 	}
3269308781adSSepherosa Ziehau 
32708892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3271308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
32729a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.lanai =
3273308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3274308781adSSepherosa Ziehau 
32758892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3276308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
32779a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.lanai =
3278308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
32798892ea20SAggelos Economopoulos 
32808892ea20SAggelos Economopoulos 	if (err != 0) {
3281308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
32828892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
32838892ea20SAggelos Economopoulos 		return EIO;
32848892ea20SAggelos Economopoulos 	}
32858892ea20SAggelos Economopoulos 
3286308781adSSepherosa Ziehau 	/*
3287308781adSSepherosa Ziehau 	 * Stock small receive ring
3288308781adSSepherosa Ziehau 	 */
32899a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
32909a4ae890SSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_data.rx_small,
32919a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map, i, TRUE);
32928892ea20SAggelos Economopoulos 		if (err) {
3293308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
32949a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.mask + 1);
32958892ea20SAggelos Economopoulos 			return ENOMEM;
32968892ea20SAggelos Economopoulos 		}
32978892ea20SAggelos Economopoulos 	}
3298308781adSSepherosa Ziehau 
3299308781adSSepherosa Ziehau 	/*
3300308781adSSepherosa Ziehau 	 * Stock big receive ring
3301308781adSSepherosa Ziehau 	 */
33029a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33039a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_low = 0xffffffff;
33049a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_high = 0xffffffff;
33058892ea20SAggelos Economopoulos 	}
3306308781adSSepherosa Ziehau 
33079a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.cl_size = cl_size;
3308308781adSSepherosa Ziehau 
33099a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33109a4ae890SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_data.rx_big,
33119a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map, i, TRUE);
33128892ea20SAggelos Economopoulos 		if (err) {
3313308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
33149a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.mask + 1);
33158892ea20SAggelos Economopoulos 			return ENOMEM;
33168892ea20SAggelos Economopoulos 		}
33178892ea20SAggelos Economopoulos 	}
33188892ea20SAggelos Economopoulos 	return 0;
33198892ea20SAggelos Economopoulos }
33208892ea20SAggelos Economopoulos 
33218892ea20SAggelos Economopoulos static int
33228892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
33238892ea20SAggelos Economopoulos {
33246ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
33258892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3326b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
33278892ea20SAggelos Economopoulos 	bus_addr_t bus;
33288892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
33298892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
33308892ea20SAggelos Economopoulos 
333126634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
33326ee6cba3SSepherosa Ziehau 
33338892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
33346ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
33358892ea20SAggelos Economopoulos 
33368892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
33378892ea20SAggelos Economopoulos 	if (err != 0) {
33386ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
33398892ea20SAggelos Economopoulos 		return EIO;
33408892ea20SAggelos Economopoulos 	}
33418892ea20SAggelos Economopoulos 
33428892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
33436ee6cba3SSepherosa Ziehau 		/* Setup the indirection table */
33448892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
33456ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
33468892ea20SAggelos Economopoulos 
33476ee6cba3SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
33488892ea20SAggelos Economopoulos 		if (err != 0) {
33496ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
33508892ea20SAggelos Economopoulos 			return err;
33518892ea20SAggelos Economopoulos 		}
33528892ea20SAggelos Economopoulos 
33536ee6cba3SSepherosa Ziehau 		/* Just enable an identity mapping */
33548892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
33558892ea20SAggelos Economopoulos 		for (i = 0; i < sc->num_slices; i++)
33568892ea20SAggelos Economopoulos 			itable[i] = (uint8_t)i;
33578892ea20SAggelos Economopoulos 
33581cd61a7cSSepherosa Ziehau 		if (sc->use_rss) {
33591cd61a7cSSepherosa Ziehau 			volatile uint8_t *hwkey;
33601cd61a7cSSepherosa Ziehau 			uint8_t swkey[MXGE_HWRSS_KEYLEN];
33611cd61a7cSSepherosa Ziehau 
33621cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_KEY_OFFSET,
33631cd61a7cSSepherosa Ziehau 			    &cmd);
33641cd61a7cSSepherosa Ziehau 			if (err != 0) {
33651cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to get rsskey\n");
33661cd61a7cSSepherosa Ziehau 				return err;
33671cd61a7cSSepherosa Ziehau 			}
33681cd61a7cSSepherosa Ziehau 			hwkey = sc->sram + cmd.data0;
33691cd61a7cSSepherosa Ziehau 
33701cd61a7cSSepherosa Ziehau 			toeplitz_get_key(swkey, MXGE_HWRSS_KEYLEN);
33711cd61a7cSSepherosa Ziehau 			for (i = 0; i < MXGE_HWRSS_KEYLEN; ++i)
33721cd61a7cSSepherosa Ziehau 				hwkey[i] = swkey[i];
33731cd61a7cSSepherosa Ziehau 			wmb();
33741cd61a7cSSepherosa Ziehau 
33751cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_RSS_KEY_UPDATED,
33761cd61a7cSSepherosa Ziehau 			    &cmd);
33771cd61a7cSSepherosa Ziehau 			if (err != 0) {
33781cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to update rsskey\n");
33791cd61a7cSSepherosa Ziehau 				return err;
33801cd61a7cSSepherosa Ziehau 			}
33811cd61a7cSSepherosa Ziehau 			if (bootverbose)
33821cd61a7cSSepherosa Ziehau 				if_printf(ifp, "RSS key updated\n");
33831cd61a7cSSepherosa Ziehau 		}
33841cd61a7cSSepherosa Ziehau 
33858892ea20SAggelos Economopoulos 		cmd.data0 = 1;
33868433e5f5SSepherosa Ziehau 		if (sc->use_rss) {
33878433e5f5SSepherosa Ziehau 			if (bootverbose)
33888433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: RSS\n");
3389e6c7b753SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_IPV4 |
3390e6c7b753SSepherosa Ziehau 			    MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
33918433e5f5SSepherosa Ziehau 		} else {
33928433e5f5SSepherosa Ziehau 			if (bootverbose)
33938433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: SRC_DST_PORT\n");
33948433e5f5SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
33958433e5f5SSepherosa Ziehau 		}
33968892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
33978892ea20SAggelos Economopoulos 		if (err != 0) {
33986ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
33998892ea20SAggelos Economopoulos 			return err;
34008892ea20SAggelos Economopoulos 		}
34018892ea20SAggelos Economopoulos 	}
34028892ea20SAggelos Economopoulos 
340389d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
340489d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
340589d55360SSepherosa Ziehau 	if (err) {
34066ee6cba3SSepherosa Ziehau 		/*
34076ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
34086ee6cba3SSepherosa Ziehau 		 */
34096ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
34106ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
34116ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
34126ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
341389d55360SSepherosa Ziehau 	}
34148892ea20SAggelos Economopoulos 
3415b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
34168892ea20SAggelos Economopoulos 
3417b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
34186ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
34196ee6cba3SSepherosa Ziehau 	/*
34206ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
34216ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
34226ee6cba3SSepherosa Ziehau 	 */
34236ee6cba3SSepherosa Ziehau 
34246ee6cba3SSepherosa Ziehau 	/*
34256ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
34266ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
34272f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
34286ee6cba3SSepherosa Ziehau 	 */
34296ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
34308892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
34316ee6cba3SSepherosa Ziehau 
343260f8c66dSSepherosa Ziehau 	cmd.data0 = MXGE_RX_SMALL_BUFLEN;
34336ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
34346ee6cba3SSepherosa Ziehau 
3435b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
34368892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
34378892ea20SAggelos Economopoulos 
34388892ea20SAggelos Economopoulos 	if (err != 0) {
34396ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
34408892ea20SAggelos Economopoulos 		goto abort;
34418892ea20SAggelos Economopoulos 	}
34428892ea20SAggelos Economopoulos 
34438892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
3444ebe934eaSSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; slice++) {
34458892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
34467cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34477cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34488892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
34498892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
34508892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
34518892ea20SAggelos Economopoulos 	}
34528892ea20SAggelos Economopoulos 
34538892ea20SAggelos Economopoulos 	if (err != 0) {
34547cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
34558892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
34568892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
34578892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
34586ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
34598892ea20SAggelos Economopoulos 		    &cmd);
34606ee6cba3SSepherosa Ziehau 
34618892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
34628892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
34638892ea20SAggelos Economopoulos 	} else {
34648892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
34658892ea20SAggelos Economopoulos 	}
34668892ea20SAggelos Economopoulos 
34678892ea20SAggelos Economopoulos 	if (err != 0) {
34686ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
34698892ea20SAggelos Economopoulos 		goto abort;
34708892ea20SAggelos Economopoulos 	}
34718892ea20SAggelos Economopoulos 
34728892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3473b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
34748892ea20SAggelos Economopoulos 		if (err != 0) {
34756ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
34768892ea20SAggelos Economopoulos 			goto abort;
34778892ea20SAggelos Economopoulos 		}
34788892ea20SAggelos Economopoulos 	}
34798892ea20SAggelos Economopoulos 
34808892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
34818892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
34828892ea20SAggelos Economopoulos 	if (err) {
34836ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
34848892ea20SAggelos Economopoulos 		goto abort;
34858892ea20SAggelos Economopoulos 	}
3486aca8f373SSepherosa Ziehau 
34876ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
3488aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3489aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3490aca8f373SSepherosa Ziehau 
3491aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3492aca8f373SSepherosa Ziehau 		ifsq_watchdog_start(&tx->watchdog);
3493aca8f373SSepherosa Ziehau 	}
34948892ea20SAggelos Economopoulos 
34958892ea20SAggelos Economopoulos 	return 0;
34968892ea20SAggelos Economopoulos 
34978892ea20SAggelos Economopoulos abort:
34988892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
34998892ea20SAggelos Economopoulos 	return err;
35008892ea20SAggelos Economopoulos }
35018892ea20SAggelos Economopoulos 
35022c29ffc6SSepherosa Ziehau static void
350389d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
35048892ea20SAggelos Economopoulos {
35052c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
35068892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3507aca8f373SSepherosa Ziehau 	int err, old_down_cnt, i;
35088892ea20SAggelos Economopoulos 
350926634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
351089d55360SSepherosa Ziehau 
351189d55360SSepherosa Ziehau 	if (!down) {
35128892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
35138892ea20SAggelos Economopoulos 		wmb();
35142c29ffc6SSepherosa Ziehau 
35158892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
35162c29ffc6SSepherosa Ziehau 		if (err)
35172c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
35182c29ffc6SSepherosa Ziehau 
35198892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
3520aca8f373SSepherosa Ziehau 			/*
3521aca8f373SSepherosa Ziehau 			 * Wait for down irq
3522aca8f373SSepherosa Ziehau 			 * XXX racy
3523aca8f373SSepherosa Ziehau 			 */
352426634ef8SSepherosa Ziehau 			ifnet_deserialize_all(ifp);
35258892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
352626634ef8SSepherosa Ziehau 			ifnet_serialize_all(ifp);
35278892ea20SAggelos Economopoulos 		}
35282c29ffc6SSepherosa Ziehau 
35298892ea20SAggelos Economopoulos 		wmb();
35302c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
35312c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
353289d55360SSepherosa Ziehau 	}
35338892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
3534aca8f373SSepherosa Ziehau 
3535aca8f373SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
3536aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3537aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3538aca8f373SSepherosa Ziehau 
3539aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3540aca8f373SSepherosa Ziehau 		ifsq_watchdog_stop(&tx->watchdog);
3541aca8f373SSepherosa Ziehau 	}
35428892ea20SAggelos Economopoulos }
35438892ea20SAggelos Economopoulos 
35448892ea20SAggelos Economopoulos static void
35458892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
35468892ea20SAggelos Economopoulos {
35478892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
35488892ea20SAggelos Economopoulos 	int reg;
354989d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
35508892ea20SAggelos Economopoulos 
35517cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
35528892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
35538892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
35548892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
35558892ea20SAggelos Economopoulos 
355689d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
35578892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
35588892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
35598892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
356089d55360SSepherosa Ziehau 			sc->pectl = pectl;
356189d55360SSepherosa Ziehau 		} else {
35627cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
356389d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
356489d55360SSepherosa Ziehau 		}
35658892ea20SAggelos Economopoulos 	}
35668892ea20SAggelos Economopoulos 
35677cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
35688892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
35698892ea20SAggelos Economopoulos }
35708892ea20SAggelos Economopoulos 
35718892ea20SAggelos Economopoulos static uint32_t
35728892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
35738892ea20SAggelos Economopoulos {
35748892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
35758892ea20SAggelos Economopoulos 	uint32_t vs;
35768892ea20SAggelos Economopoulos 
35778a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
35788892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
35798a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
35808892ea20SAggelos Economopoulos 		return (uint32_t)-1;
35818892ea20SAggelos Economopoulos 	}
35828a20b038SSepherosa Ziehau 	/* Enable read32 mode */
35838892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
35848a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
35858892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
35868a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
35878892ea20SAggelos Economopoulos }
35888892ea20SAggelos Economopoulos 
358989d55360SSepherosa Ziehau static void
359089d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
35918892ea20SAggelos Economopoulos {
35928892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
359389d55360SSepherosa Ziehau 	int err, running;
35948892ea20SAggelos Economopoulos 	uint32_t reboot;
35958892ea20SAggelos Economopoulos 	uint16_t cmd;
35968892ea20SAggelos Economopoulos 
35978892ea20SAggelos Economopoulos 	err = ENXIO;
35988892ea20SAggelos Economopoulos 
35998a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
36008892ea20SAggelos Economopoulos 
36018892ea20SAggelos Economopoulos 	/*
36028a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
36038892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
36048892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
36058892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
36068892ea20SAggelos Economopoulos 	 * again
36078892ea20SAggelos Economopoulos 	 */
36088892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36098892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
36108892ea20SAggelos Economopoulos 		/*
36118a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
36128892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
36138892ea20SAggelos Economopoulos 		 * back, then give up
36148892ea20SAggelos Economopoulos 		 */
36158892ea20SAggelos Economopoulos 		DELAY(1000*100);
36168892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36178a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
36188a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
36198892ea20SAggelos Economopoulos 	}
36208892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
36218a20b038SSepherosa Ziehau 		/* Print the reboot status */
36228892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
36238a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
36248a20b038SSepherosa Ziehau 
362589d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
362689d55360SSepherosa Ziehau 		if (running) {
362789d55360SSepherosa Ziehau 			/*
36288a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
362989d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
363089d55360SSepherosa Ziehau 			 */
363189d55360SSepherosa Ziehau 
363289d55360SSepherosa Ziehau 			/* Mark the link as down */
363389d55360SSepherosa Ziehau 			if (sc->link_state) {
363489d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
363589d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
363689d55360SSepherosa Ziehau 			}
363789d55360SSepherosa Ziehau 			mxge_close(sc, 1);
363889d55360SSepherosa Ziehau 		}
36398a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
36408892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
36418892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
36428892ea20SAggelos Economopoulos 
36438a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
36448892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
36458892ea20SAggelos Economopoulos 
36468a20b038SSepherosa Ziehau 		/* Reload f/w */
364789d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
36488a20b038SSepherosa Ziehau 		if (err)
36498a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
36508a20b038SSepherosa Ziehau 		if (running && !err) {
3651aca8f373SSepherosa Ziehau 			int i;
3652aca8f373SSepherosa Ziehau 
365389d55360SSepherosa Ziehau 			err = mxge_open(sc);
3654aca8f373SSepherosa Ziehau 
3655aca8f373SSepherosa Ziehau 			for (i = 0; i < sc->num_tx_rings; ++i)
3656aca8f373SSepherosa Ziehau 				ifsq_devstart_sched(sc->ss[i].tx.ifsq);
365789d55360SSepherosa Ziehau 		}
365889d55360SSepherosa Ziehau 		sc->watchdog_resets++;
365989d55360SSepherosa Ziehau 	} else {
36608a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
366189d55360SSepherosa Ziehau 		err = 0;
366289d55360SSepherosa Ziehau 	}
366389d55360SSepherosa Ziehau 	if (err) {
36648a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
366589d55360SSepherosa Ziehau 	} else {
366689d55360SSepherosa Ziehau 		if (sc->dying == 2)
366789d55360SSepherosa Ziehau 			sc->dying = 0;
366889d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
366989d55360SSepherosa Ziehau 	}
367089d55360SSepherosa Ziehau }
367189d55360SSepherosa Ziehau 
367289d55360SSepherosa Ziehau static void
367389d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
367489d55360SSepherosa Ziehau {
36758a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
36768a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
36778892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
36788a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
36798892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
36808a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
36818a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
36828892ea20SAggelos Economopoulos }
36838892ea20SAggelos Economopoulos 
368489d55360SSepherosa Ziehau static u_long
36858892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
36868892ea20SAggelos Economopoulos {
3687cc9c62a4SSepherosa Ziehau 	u_long ipackets, opackets, pkts;
36888892ea20SAggelos Economopoulos 
3689cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, ipackets);
3690cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, opackets);
369189d55360SSepherosa Ziehau 
3692cc9c62a4SSepherosa Ziehau 	pkts = ipackets - sc->ipackets;
3693cc9c62a4SSepherosa Ziehau 	pkts += opackets - sc->opackets;
369489d55360SSepherosa Ziehau 
3695cc9c62a4SSepherosa Ziehau 	sc->ipackets = ipackets;
3696cc9c62a4SSepherosa Ziehau 	sc->opackets = opackets;
3697cc9c62a4SSepherosa Ziehau 
369889d55360SSepherosa Ziehau 	return pkts;
36998892ea20SAggelos Economopoulos }
37008892ea20SAggelos Economopoulos 
37018892ea20SAggelos Economopoulos static void
37028892ea20SAggelos Economopoulos mxge_tick(void *arg)
37038892ea20SAggelos Economopoulos {
37048892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
370589d55360SSepherosa Ziehau 	u_long pkts = 0;
37068892ea20SAggelos Economopoulos 	int err = 0;
3707ca8ca004SSepherosa Ziehau 	int ticks;
37088892ea20SAggelos Economopoulos 
370926634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&sc->main_serialize);
371089d55360SSepherosa Ziehau 
371189d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3712ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3713ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
371489d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3715ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3716ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
371789d55360SSepherosa Ziehau 	}
371889d55360SSepherosa Ziehau 	if (pkts == 0) {
3719cc9c62a4SSepherosa Ziehau 		uint16_t cmd;
3720cc9c62a4SSepherosa Ziehau 
3721ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
372289d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
372389d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
372489d55360SSepherosa Ziehau 			sc->dying = 2;
372526634ef8SSepherosa Ziehau 			mxge_serialize_skipmain(sc);
372689d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
372726634ef8SSepherosa Ziehau 			mxge_deserialize_skipmain(sc);
372889d55360SSepherosa Ziehau 			err = ENXIO;
372989d55360SSepherosa Ziehau 		}
3730cc9c62a4SSepherosa Ziehau 
3731ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
373289d55360SSepherosa Ziehau 		ticks *= 4;
373389d55360SSepherosa Ziehau 	}
373489d55360SSepherosa Ziehau 
37358892ea20SAggelos Economopoulos 	if (err == 0)
373689d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
373789d55360SSepherosa Ziehau 
373826634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&sc->main_serialize);
37398892ea20SAggelos Economopoulos }
37408892ea20SAggelos Economopoulos 
37418892ea20SAggelos Economopoulos static int
37428892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
37438892ea20SAggelos Economopoulos {
374400f2de12SSepherosa Ziehau 	mxge_softc_t *sc = ifp->if_softc;
374500f2de12SSepherosa Ziehau 	const struct ifmedia *ifm = &sc->media;
374600f2de12SSepherosa Ziehau 	int pause;
374700f2de12SSepherosa Ziehau 
374800f2de12SSepherosa Ziehau 	if (IFM_OPTIONS(ifm->ifm_media) & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE)) {
374900f2de12SSepherosa Ziehau 		if (sc->pause)
375000f2de12SSepherosa Ziehau 			return 0;
375100f2de12SSepherosa Ziehau 		pause = 1;
375200f2de12SSepherosa Ziehau 	} else {
375300f2de12SSepherosa Ziehau 		if (!sc->pause)
375400f2de12SSepherosa Ziehau 			return 0;
375500f2de12SSepherosa Ziehau 		pause = 0;
375600f2de12SSepherosa Ziehau 	}
375700f2de12SSepherosa Ziehau 	return mxge_change_pause(sc, pause);
37588892ea20SAggelos Economopoulos }
37598892ea20SAggelos Economopoulos 
37608892ea20SAggelos Economopoulos static int
37618892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
37628892ea20SAggelos Economopoulos {
37638892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
37648892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
37658892ea20SAggelos Economopoulos 	int err = 0;
37668892ea20SAggelos Economopoulos 
3767b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3768b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
37698892ea20SAggelos Economopoulos 		return EINVAL;
3770b9a8961fSSepherosa Ziehau 
37718892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
37728892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
37732ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
377489d55360SSepherosa Ziehau 		mxge_close(sc, 0);
37758892ea20SAggelos Economopoulos 		err = mxge_open(sc);
37768892ea20SAggelos Economopoulos 		if (err != 0) {
37778892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
377889d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3779b9a8961fSSepherosa Ziehau 			mxge_open(sc);
37808892ea20SAggelos Economopoulos 		}
37818892ea20SAggelos Economopoulos 	}
37828892ea20SAggelos Economopoulos 	return err;
37838892ea20SAggelos Economopoulos }
37848892ea20SAggelos Economopoulos 
37858892ea20SAggelos Economopoulos static void
37868892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
37878892ea20SAggelos Economopoulos {
37888892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
37898892ea20SAggelos Economopoulos 
37908892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
3791166c46afSSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
3792166c46afSSepherosa Ziehau 
3793dbf0d419SSepherosa Ziehau 	if (sc->link_state)
3794166c46afSSepherosa Ziehau 		ifmr->ifm_status |= IFM_ACTIVE;
3795166c46afSSepherosa Ziehau 
3796dbf0d419SSepherosa Ziehau 	/*
3797dbf0d419SSepherosa Ziehau 	 * Autoselect is not supported, so the current media
3798dbf0d419SSepherosa Ziehau 	 * should be delivered.
3799dbf0d419SSepherosa Ziehau 	 */
380089d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
380100f2de12SSepherosa Ziehau 	if (sc->current_media != IFM_NONE) {
380200f2de12SSepherosa Ziehau 		ifmr->ifm_active |= MXGE_IFM;
380300f2de12SSepherosa Ziehau 		if (sc->pause)
380400f2de12SSepherosa Ziehau 			ifmr->ifm_active |= IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
380500f2de12SSepherosa Ziehau 	}
38068892ea20SAggelos Economopoulos }
38078892ea20SAggelos Economopoulos 
38088892ea20SAggelos Economopoulos static int
380989d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
381089d55360SSepherosa Ziehau     struct ucred *cr __unused)
38118892ea20SAggelos Economopoulos {
38128892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38138892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
38148892ea20SAggelos Economopoulos 	int err, mask;
38158892ea20SAggelos Economopoulos 
381626634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
3817af85d4d5SSepherosa Ziehau 	err = 0;
3818af85d4d5SSepherosa Ziehau 
38198892ea20SAggelos Economopoulos 	switch (command) {
38208892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
38218892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
38228892ea20SAggelos Economopoulos 		break;
38238892ea20SAggelos Economopoulos 
38248892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3825af85d4d5SSepherosa Ziehau 		if (sc->dying)
38268892ea20SAggelos Economopoulos 			return EINVAL;
3827af85d4d5SSepherosa Ziehau 
38288892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
38292ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
38308892ea20SAggelos Economopoulos 				err = mxge_open(sc);
38318892ea20SAggelos Economopoulos 			} else {
3832af85d4d5SSepherosa Ziehau 				/*
3833af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3834af85d4d5SSepherosa Ziehau 				 * flag changes
3835af85d4d5SSepherosa Ziehau 				 */
38368892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
38378892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
38388892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
38398892ea20SAggelos Economopoulos 			}
38408892ea20SAggelos Economopoulos 		} else {
3841af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
384289d55360SSepherosa Ziehau 				mxge_close(sc, 0);
38438892ea20SAggelos Economopoulos 		}
38448892ea20SAggelos Economopoulos 		break;
38458892ea20SAggelos Economopoulos 
38468892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
38478892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
38488892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
38498892ea20SAggelos Economopoulos 		break;
38508892ea20SAggelos Economopoulos 
38518892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
38528892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
38538892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
385489d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
385589d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
385689d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
38578892ea20SAggelos Economopoulos 			else
385889d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
38598892ea20SAggelos Economopoulos 		}
386089d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
386189d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
386289d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
386389d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
386489d55360SSepherosa Ziehau 			else
386589d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
386689d55360SSepherosa Ziehau 		}
386789d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
386889d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
38698892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
38708892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
38718892ea20SAggelos Economopoulos 		break;
38728892ea20SAggelos Economopoulos 
38738892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
387400f2de12SSepherosa Ziehau 	case SIOCSIFMEDIA:
38758892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
38768892ea20SAggelos Economopoulos 		    &sc->media, command);
38778892ea20SAggelos Economopoulos 		break;
38788892ea20SAggelos Economopoulos 
38798892ea20SAggelos Economopoulos 	default:
388089d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
388189d55360SSepherosa Ziehau 		break;
38828892ea20SAggelos Economopoulos 	}
38838892ea20SAggelos Economopoulos 	return err;
38848892ea20SAggelos Economopoulos }
38858892ea20SAggelos Economopoulos 
38868892ea20SAggelos Economopoulos static void
38878892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
38888892ea20SAggelos Economopoulos {
388900f2de12SSepherosa Ziehau 	int ifm;
389000f2de12SSepherosa Ziehau 
38917cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
38927cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
38937cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
38948892ea20SAggelos Economopoulos 
38957cc92483SSepherosa Ziehau 	/* XXX */
38968892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
38978892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
38987cc92483SSepherosa Ziehau 
389900f2de12SSepherosa Ziehau 	ifm = ifmedia_str2ethfc(mxge_flowctrl);
390000f2de12SSepherosa Ziehau 	if (ifm & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE))
390100f2de12SSepherosa Ziehau 		sc->pause = 1;
390200f2de12SSepherosa Ziehau 
39038433e5f5SSepherosa Ziehau 	sc->use_rss = mxge_use_rss;
39048892ea20SAggelos Economopoulos 
390589d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
39067cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
39077cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
39087cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
39097cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
391089d55360SSepherosa Ziehau }
39118892ea20SAggelos Economopoulos 
39128892ea20SAggelos Economopoulos static void
39138892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
39148892ea20SAggelos Economopoulos {
39158892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39168892ea20SAggelos Economopoulos 	int i;
39178892ea20SAggelos Economopoulos 
39188892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
39198892ea20SAggelos Economopoulos 		return;
39208892ea20SAggelos Economopoulos 
39218892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39228892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39238892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
39248892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
39258892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
39268892ea20SAggelos Economopoulos 		}
39279a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_done.entry != NULL) {
3928414caf0dSSepherosa Ziehau 			mxge_dma_free(&ss->rx_done_dma);
39299a4ae890SSepherosa Ziehau 			ss->rx_data.rx_done.entry = NULL;
39308892ea20SAggelos Economopoulos 		}
39318892ea20SAggelos Economopoulos 	}
39326c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
39338892ea20SAggelos Economopoulos 	sc->ss = NULL;
39348892ea20SAggelos Economopoulos }
39358892ea20SAggelos Economopoulos 
39368892ea20SAggelos Economopoulos static int
39378892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
39388892ea20SAggelos Economopoulos {
39398892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
39408892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39418892ea20SAggelos Economopoulos 	size_t bytes;
3942089301c2SSepherosa Ziehau 	int err, i, rx_ring_size;
39438892ea20SAggelos Economopoulos 
39448892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
39458892ea20SAggelos Economopoulos 	if (err != 0) {
39468892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
39478892ea20SAggelos Economopoulos 		return err;
39488892ea20SAggelos Economopoulos 	}
3949089301c2SSepherosa Ziehau 	rx_ring_size = cmd.data0;
3950089301c2SSepherosa Ziehau 	sc->rx_intr_slots = 2 * (rx_ring_size / sizeof (mcp_dma_addr_t));
39518892ea20SAggelos Economopoulos 
39528892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
3953dfda108aSSepherosa Ziehau 	sc->ss = kmalloc_cachealign(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
39547cc92483SSepherosa Ziehau 
39558892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39568892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39578892ea20SAggelos Economopoulos 
39588892ea20SAggelos Economopoulos 		ss->sc = sc;
39598892ea20SAggelos Economopoulos 
396026634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->rx_data.rx_serialize);
396126634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->tx.tx_serialize);
3962e6c7b753SSepherosa Ziehau 		ss->intr_rid = -1;
396326634ef8SSepherosa Ziehau 
39647cc92483SSepherosa Ziehau 		/*
3965e6c7b753SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queue
3966089301c2SSepherosa Ziehau 		 * XXX assume 4bytes mcp_slot
39677cc92483SSepherosa Ziehau 		 */
3968089301c2SSepherosa Ziehau 		bytes = sc->rx_intr_slots * sizeof(mcp_slot_t);
3969414caf0dSSepherosa Ziehau 		err = mxge_dma_alloc(sc, &ss->rx_done_dma, bytes, 4096);
3970798c3369SSepherosa Ziehau 		if (err != 0) {
3971798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3972798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3973798c3369SSepherosa Ziehau 			return err;
3974798c3369SSepherosa Ziehau 		}
3975414caf0dSSepherosa Ziehau 		ss->rx_data.rx_done.entry = ss->rx_done_dma.dmem_addr;
39768892ea20SAggelos Economopoulos 
39778892ea20SAggelos Economopoulos 		/*
3978ebe934eaSSepherosa Ziehau 		 * Allocate the per-slice firmware stats
39798892ea20SAggelos Economopoulos 		 */
39808892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
39818892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
39828892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3983798c3369SSepherosa Ziehau 		if (err != 0) {
3984798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3985798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3986798c3369SSepherosa Ziehau 			return err;
3987798c3369SSepherosa Ziehau 		}
39887cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
39898892ea20SAggelos Economopoulos 	}
39907cc92483SSepherosa Ziehau 	return 0;
39918892ea20SAggelos Economopoulos }
39928892ea20SAggelos Economopoulos 
39938892ea20SAggelos Economopoulos static void
39948892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
39958892ea20SAggelos Economopoulos {
3996e6c7b753SSepherosa Ziehau 	int status, max_intr_slots, max_slices, num_slices;
3997aca8f373SSepherosa Ziehau 	int msix_cnt, msix_enable, i, multi_tx;
39988892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3999c7431c78SSepherosa Ziehau 	const char *old_fw;
40008892ea20SAggelos Economopoulos 
40018892ea20SAggelos Economopoulos 	sc->num_slices = 1;
4002aca8f373SSepherosa Ziehau 	sc->num_tx_rings = 1;
40037cc92483SSepherosa Ziehau 
4004e6c7b753SSepherosa Ziehau 	num_slices = device_getenv_int(sc->dev, "num_slices", mxge_num_slices);
4005e6c7b753SSepherosa Ziehau 	if (num_slices == 1)
40068892ea20SAggelos Economopoulos 		return;
40078892ea20SAggelos Economopoulos 
4008e6c7b753SSepherosa Ziehau 	if (ncpus2 == 1)
4009e6c7b753SSepherosa Ziehau 		return;
4010e6c7b753SSepherosa Ziehau 
4011e6c7b753SSepherosa Ziehau 	msix_enable = device_getenv_int(sc->dev, "msix.enable",
4012e6c7b753SSepherosa Ziehau 	    mxge_msix_enable);
4013e6c7b753SSepherosa Ziehau 	if (!msix_enable)
4014e6c7b753SSepherosa Ziehau 		return;
4015e6c7b753SSepherosa Ziehau 
40168892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
40178892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
40188892ea20SAggelos Economopoulos 		return;
40198892ea20SAggelos Economopoulos 
4020e6c7b753SSepherosa Ziehau 	/*
4021e6c7b753SSepherosa Ziehau 	 * Round down MSI-X vector count to the nearest power of 2
4022e6c7b753SSepherosa Ziehau 	 */
4023e6c7b753SSepherosa Ziehau 	i = 0;
4024e6c7b753SSepherosa Ziehau 	while ((1 << (i + 1)) <= msix_cnt)
4025e6c7b753SSepherosa Ziehau 		++i;
4026e6c7b753SSepherosa Ziehau 	msix_cnt = 1 << i;
4027e6c7b753SSepherosa Ziehau 
4028e6c7b753SSepherosa Ziehau 	/*
4029e6c7b753SSepherosa Ziehau 	 * Now load the slice aware firmware see what it supports
4030e6c7b753SSepherosa Ziehau 	 */
40318892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
40328892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
40338892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
40348892ea20SAggelos Economopoulos 	else
40358892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
40368892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
40378892ea20SAggelos Economopoulos 	if (status != 0) {
40388892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
40398892ea20SAggelos Economopoulos 		return;
40408892ea20SAggelos Economopoulos 	}
40418892ea20SAggelos Economopoulos 
4042e6c7b753SSepherosa Ziehau 	/*
4043e6c7b753SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it is alive
4044e6c7b753SSepherosa Ziehau 	 */
40458892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof(cmd));
40468892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
40478892ea20SAggelos Economopoulos 	if (status != 0) {
40488892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
40498892ea20SAggelos Economopoulos 		goto abort_with_fw;
40508892ea20SAggelos Economopoulos 	}
40518892ea20SAggelos Economopoulos 
4052e6c7b753SSepherosa Ziehau 	/*
4053e6c7b753SSepherosa Ziehau 	 * Get rx ring size to calculate rx interrupt queue size
4054e6c7b753SSepherosa Ziehau 	 */
40558892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40568892ea20SAggelos Economopoulos 	if (status != 0) {
40578892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
40588892ea20SAggelos Economopoulos 		goto abort_with_fw;
40598892ea20SAggelos Economopoulos 	}
40608892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof(mcp_dma_addr_t));
40618892ea20SAggelos Economopoulos 
4062e6c7b753SSepherosa Ziehau 	/*
4063e6c7b753SSepherosa Ziehau 	 * Tell it the size of the rx interrupt queue
4064e6c7b753SSepherosa Ziehau 	 */
40658892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof(struct mcp_slot);
40668892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
40678892ea20SAggelos Economopoulos 	if (status != 0) {
40688892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
40698892ea20SAggelos Economopoulos 		goto abort_with_fw;
40708892ea20SAggelos Economopoulos 	}
40718892ea20SAggelos Economopoulos 
4072e6c7b753SSepherosa Ziehau 	/*
4073e6c7b753SSepherosa Ziehau 	 * Ask the maximum number of slices it supports
4074e6c7b753SSepherosa Ziehau 	 */
40758892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
40768892ea20SAggelos Economopoulos 	if (status != 0) {
40778892ea20SAggelos Economopoulos 		device_printf(sc->dev,
40788892ea20SAggelos Economopoulos 		    "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
40798892ea20SAggelos Economopoulos 		goto abort_with_fw;
40808892ea20SAggelos Economopoulos 	}
4081e6c7b753SSepherosa Ziehau 	max_slices = cmd.data0;
40828892ea20SAggelos Economopoulos 
4083e6c7b753SSepherosa Ziehau 	/*
4084e6c7b753SSepherosa Ziehau 	 * Round down max slices count to the nearest power of 2
4085e6c7b753SSepherosa Ziehau 	 */
4086e6c7b753SSepherosa Ziehau 	i = 0;
4087e6c7b753SSepherosa Ziehau 	while ((1 << (i + 1)) <= max_slices)
4088e6c7b753SSepherosa Ziehau 		++i;
4089e6c7b753SSepherosa Ziehau 	max_slices = 1 << i;
4090e6c7b753SSepherosa Ziehau 
4091e6c7b753SSepherosa Ziehau 	if (max_slices > msix_cnt)
4092e6c7b753SSepherosa Ziehau 		max_slices = msix_cnt;
4093e6c7b753SSepherosa Ziehau 
4094e6c7b753SSepherosa Ziehau 	sc->num_slices = num_slices;
4095e6c7b753SSepherosa Ziehau 	sc->num_slices = if_ring_count2(sc->num_slices, max_slices);
4096e6c7b753SSepherosa Ziehau 
4097aca8f373SSepherosa Ziehau 	multi_tx = device_getenv_int(sc->dev, "multi_tx", mxge_multi_tx);
4098aca8f373SSepherosa Ziehau 	if (multi_tx)
4099aca8f373SSepherosa Ziehau 		sc->num_tx_rings = sc->num_slices;
4100aca8f373SSepherosa Ziehau 
4101e6c7b753SSepherosa Ziehau 	if (bootverbose) {
4102e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "using %d slices, max %d\n",
4103e6c7b753SSepherosa Ziehau 		    sc->num_slices, max_slices);
41048892ea20SAggelos Economopoulos 	}
41058892ea20SAggelos Economopoulos 
4106e6c7b753SSepherosa Ziehau 	if (sc->num_slices == 1)
4107e6c7b753SSepherosa Ziehau 		goto abort_with_fw;
41088892ea20SAggelos Economopoulos 	return;
41098892ea20SAggelos Economopoulos 
41108892ea20SAggelos Economopoulos abort_with_fw:
41118892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
4112e6c7b753SSepherosa Ziehau 	mxge_load_firmware(sc, 0);
41138892ea20SAggelos Economopoulos }
41148892ea20SAggelos Economopoulos 
411526634ef8SSepherosa Ziehau static void
411626634ef8SSepherosa Ziehau mxge_setup_serialize(struct mxge_softc *sc)
411726634ef8SSepherosa Ziehau {
411826634ef8SSepherosa Ziehau 	int i = 0, slice;
411926634ef8SSepherosa Ziehau 
412026634ef8SSepherosa Ziehau 	/* Main + rx + tx */
412126634ef8SSepherosa Ziehau 	sc->nserialize = (2 * sc->num_slices) + 1;
412226634ef8SSepherosa Ziehau 	sc->serializes =
412326634ef8SSepherosa Ziehau 	    kmalloc(sc->nserialize * sizeof(struct lwkt_serialize *),
412426634ef8SSepherosa Ziehau 	        M_DEVBUF, M_WAITOK | M_ZERO);
412526634ef8SSepherosa Ziehau 
412626634ef8SSepherosa Ziehau 	/*
412726634ef8SSepherosa Ziehau 	 * Setup serializes
412826634ef8SSepherosa Ziehau 	 *
412926634ef8SSepherosa Ziehau 	 * NOTE: Order is critical
413026634ef8SSepherosa Ziehau 	 */
413126634ef8SSepherosa Ziehau 
413226634ef8SSepherosa Ziehau 	KKASSERT(i < sc->nserialize);
413326634ef8SSepherosa Ziehau 	sc->serializes[i++] = &sc->main_serialize;
413426634ef8SSepherosa Ziehau 
413526634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
413626634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
413726634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].rx_data.rx_serialize;
413826634ef8SSepherosa Ziehau 	}
413926634ef8SSepherosa Ziehau 
414026634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
414126634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
414226634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].tx.tx_serialize;
414326634ef8SSepherosa Ziehau 	}
414426634ef8SSepherosa Ziehau 
414526634ef8SSepherosa Ziehau 	KKASSERT(i == sc->nserialize);
414626634ef8SSepherosa Ziehau }
414726634ef8SSepherosa Ziehau 
414826634ef8SSepherosa Ziehau static void
414926634ef8SSepherosa Ziehau mxge_serialize(struct ifnet *ifp, enum ifnet_serialize slz)
415026634ef8SSepherosa Ziehau {
415126634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
415226634ef8SSepherosa Ziehau 
415326634ef8SSepherosa Ziehau 	ifnet_serialize_array_enter(sc->serializes, sc->nserialize, slz);
415426634ef8SSepherosa Ziehau }
415526634ef8SSepherosa Ziehau 
415626634ef8SSepherosa Ziehau static void
415726634ef8SSepherosa Ziehau mxge_deserialize(struct ifnet *ifp, enum ifnet_serialize slz)
415826634ef8SSepherosa Ziehau {
415926634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
416026634ef8SSepherosa Ziehau 
416126634ef8SSepherosa Ziehau 	ifnet_serialize_array_exit(sc->serializes, sc->nserialize, slz);
416226634ef8SSepherosa Ziehau }
416326634ef8SSepherosa Ziehau 
416426634ef8SSepherosa Ziehau static int
416526634ef8SSepherosa Ziehau mxge_tryserialize(struct ifnet *ifp, enum ifnet_serialize slz)
416626634ef8SSepherosa Ziehau {
416726634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
416826634ef8SSepherosa Ziehau 
416926634ef8SSepherosa Ziehau 	return ifnet_serialize_array_try(sc->serializes, sc->nserialize, slz);
417026634ef8SSepherosa Ziehau }
417126634ef8SSepherosa Ziehau 
417226634ef8SSepherosa Ziehau #ifdef INVARIANTS
417326634ef8SSepherosa Ziehau 
417426634ef8SSepherosa Ziehau static void
417526634ef8SSepherosa Ziehau mxge_serialize_assert(struct ifnet *ifp, enum ifnet_serialize slz,
417626634ef8SSepherosa Ziehau     boolean_t serialized)
417726634ef8SSepherosa Ziehau {
417826634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
417926634ef8SSepherosa Ziehau 
418026634ef8SSepherosa Ziehau 	ifnet_serialize_array_assert(sc->serializes, sc->nserialize,
418126634ef8SSepherosa Ziehau 	    slz, serialized);
418226634ef8SSepherosa Ziehau }
418326634ef8SSepherosa Ziehau 
418426634ef8SSepherosa Ziehau #endif	/* INVARIANTS */
418526634ef8SSepherosa Ziehau 
41862276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
41872276707eSSepherosa Ziehau 
41882276707eSSepherosa Ziehau static void
41892276707eSSepherosa Ziehau mxge_npoll_rx(struct ifnet *ifp, void *xss, int cycle)
41902276707eSSepherosa Ziehau {
41912276707eSSepherosa Ziehau 	struct mxge_slice_state *ss = xss;
41922276707eSSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
41932276707eSSepherosa Ziehau 
41942276707eSSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
41952276707eSSepherosa Ziehau 
41962276707eSSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0) {
41972276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, cycle);
41982276707eSSepherosa Ziehau 	} else {
41992276707eSSepherosa Ziehau 		/*
42002276707eSSepherosa Ziehau 		 * XXX
42012276707eSSepherosa Ziehau 		 * This register writting obviously has cost,
42022276707eSSepherosa Ziehau 		 * however, if we don't hand back the rx token,
42032276707eSSepherosa Ziehau 		 * the upcoming packets may suffer rediculously
42042276707eSSepherosa Ziehau 		 * large delay, as observed on 8AL-C using ping(8).
42052276707eSSepherosa Ziehau 		 */
42062276707eSSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
42072276707eSSepherosa Ziehau 	}
42082276707eSSepherosa Ziehau }
42092276707eSSepherosa Ziehau 
42102276707eSSepherosa Ziehau static void
42112276707eSSepherosa Ziehau mxge_npoll(struct ifnet *ifp, struct ifpoll_info *info)
42122276707eSSepherosa Ziehau {
42132276707eSSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
42142276707eSSepherosa Ziehau 	int i;
42152276707eSSepherosa Ziehau 
42162276707eSSepherosa Ziehau 	if (info == NULL)
42172276707eSSepherosa Ziehau 		return;
42182276707eSSepherosa Ziehau 
42192276707eSSepherosa Ziehau 	/*
42202276707eSSepherosa Ziehau 	 * Only poll rx; polling tx and status don't seem to work
42212276707eSSepherosa Ziehau 	 */
42222276707eSSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
42232276707eSSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
42242276707eSSepherosa Ziehau 		int idx = ss->intr_cpuid;
42252276707eSSepherosa Ziehau 
42262276707eSSepherosa Ziehau 		KKASSERT(idx < ncpus2);
42272276707eSSepherosa Ziehau 		info->ifpi_rx[idx].poll_func = mxge_npoll_rx;
42282276707eSSepherosa Ziehau 		info->ifpi_rx[idx].arg = ss;
42292276707eSSepherosa Ziehau 		info->ifpi_rx[idx].serializer = &ss->rx_data.rx_serialize;
42302276707eSSepherosa Ziehau 	}
42312276707eSSepherosa Ziehau }
42322276707eSSepherosa Ziehau 
42332276707eSSepherosa Ziehau #endif	/* IFPOLL_ENABLE */
42342276707eSSepherosa Ziehau 
42358892ea20SAggelos Economopoulos static int
42368892ea20SAggelos Economopoulos mxge_attach(device_t dev)
42378892ea20SAggelos Economopoulos {
42388892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4239137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
4240aca8f373SSepherosa Ziehau 	int err, rid, i;
42418892ea20SAggelos Economopoulos 
4242f0115d64SAggelos Economopoulos 	/*
42437cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4244f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4245f0115d64SAggelos Economopoulos 	 */
4246f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
42478892ea20SAggelos Economopoulos 	sc->dev = dev;
42487cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
424900f2de12SSepherosa Ziehau 
425000f2de12SSepherosa Ziehau 	/* IFM_ETH_FORCEPAUSE can't be changed */
425100f2de12SSepherosa Ziehau 	ifmedia_init(&sc->media, IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE,
425200f2de12SSepherosa Ziehau 	    mxge_media_change, mxge_media_status);
42537cc92483SSepherosa Ziehau 
425426634ef8SSepherosa Ziehau 	lwkt_serialize_init(&sc->main_serialize);
425526634ef8SSepherosa Ziehau 
42568892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
42578892ea20SAggelos Economopoulos 
42588892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
42598892ea20SAggelos Economopoulos 				 1,			/* alignment */
42608892ea20SAggelos Economopoulos 				 0,			/* boundary */
42618892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
42628892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
42638892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
42647cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
42657cc92483SSepherosa Ziehau 				 0, 			/* num segs */
42667cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
42678892ea20SAggelos Economopoulos 				 0,			/* flags */
42688892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
42698892ea20SAggelos Economopoulos 	if (err != 0) {
4270798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4271798c3369SSepherosa Ziehau 		goto failed;
42728892ea20SAggelos Economopoulos 	}
42738892ea20SAggelos Economopoulos 
4274e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
42758892ea20SAggelos Economopoulos 
42768892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
42778892ea20SAggelos Economopoulos 
42787cc92483SSepherosa Ziehau 	/*
42797cc92483SSepherosa Ziehau 	 * Map the board into the kernel
42807cc92483SSepherosa Ziehau 	 */
42818892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
42827cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
42837cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
42848892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
42858892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
42868892ea20SAggelos Economopoulos 		err = ENXIO;
4287798c3369SSepherosa Ziehau 		goto failed;
42888892ea20SAggelos Economopoulos 	}
42897cc92483SSepherosa Ziehau 
42908892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
42918892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
42928892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
42938892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
42948892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
42958892ea20SAggelos Economopoulos 		err = ENXIO;
4296798c3369SSepherosa Ziehau 		goto failed;
42978892ea20SAggelos Economopoulos 	}
42988892ea20SAggelos Economopoulos 
42997cc92483SSepherosa Ziehau 	/*
43007cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
43017cc92483SSepherosa Ziehau 	 * lanai SRAM
43027cc92483SSepherosa Ziehau 	 */
43038892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
43048892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
43058892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
43068892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
43077cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
43088892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4309798c3369SSepherosa Ziehau 	if (err != 0) {
4310798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4311798c3369SSepherosa Ziehau 		goto failed;
4312798c3369SSepherosa Ziehau 	}
43138892ea20SAggelos Economopoulos 
43147cc92483SSepherosa Ziehau 	/*
43157cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
43167cc92483SSepherosa Ziehau 	 */
43178892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
43188892ea20SAggelos Economopoulos 
43197cc92483SSepherosa Ziehau 	/*
43207cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
43217cc92483SSepherosa Ziehau 	 */
43227cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4323798c3369SSepherosa Ziehau 	if (err != 0) {
4324798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4325798c3369SSepherosa Ziehau 		goto failed;
4326798c3369SSepherosa Ziehau 	}
43277cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
43287cc92483SSepherosa Ziehau 
43298892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4330798c3369SSepherosa Ziehau 	if (err != 0) {
4331798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4332798c3369SSepherosa Ziehau 		goto failed;
4333798c3369SSepherosa Ziehau 	}
43348892ea20SAggelos Economopoulos 
43358892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4336798c3369SSepherosa Ziehau 	if (err != 0) {
4337798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4338798c3369SSepherosa Ziehau 		goto failed;
4339798c3369SSepherosa Ziehau 	}
43408892ea20SAggelos Economopoulos 
43417cc92483SSepherosa Ziehau 	/* Select & load the firmware */
43428892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4343798c3369SSepherosa Ziehau 	if (err != 0) {
4344798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4345798c3369SSepherosa Ziehau 		goto failed;
4346798c3369SSepherosa Ziehau 	}
43478892ea20SAggelos Economopoulos 
43488892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
43498892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4350798c3369SSepherosa Ziehau 	if (err != 0) {
4351798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4352798c3369SSepherosa Ziehau 		goto failed;
4353798c3369SSepherosa Ziehau 	}
43548892ea20SAggelos Economopoulos 
4355e6c7b753SSepherosa Ziehau 	err = mxge_alloc_intr(sc);
4356e6c7b753SSepherosa Ziehau 	if (err != 0) {
4357e6c7b753SSepherosa Ziehau 		device_printf(dev, "alloc intr failed\n");
4358e6c7b753SSepherosa Ziehau 		goto failed;
4359e6c7b753SSepherosa Ziehau 	}
4360e6c7b753SSepherosa Ziehau 
436126634ef8SSepherosa Ziehau 	/* Setup serializes */
436226634ef8SSepherosa Ziehau 	mxge_setup_serialize(sc);
436326634ef8SSepherosa Ziehau 
43648892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4365798c3369SSepherosa Ziehau 	if (err != 0) {
4366798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4367798c3369SSepherosa Ziehau 		goto failed;
4368798c3369SSepherosa Ziehau 	}
43698892ea20SAggelos Economopoulos 
43708892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
43718892ea20SAggelos Economopoulos 	if (err != 0) {
4372798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4373798c3369SSepherosa Ziehau 		goto failed;
43748892ea20SAggelos Economopoulos 	}
43758892ea20SAggelos Economopoulos 
43768892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
437789d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
43788892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
437989d55360SSepherosa Ziehau 
438089d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
438189d55360SSepherosa Ziehau #if 0
438289d55360SSepherosa Ziehau 	/* Well, its software, sigh */
438389d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
438489d55360SSepherosa Ziehau #endif
43858892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
438689d55360SSepherosa Ziehau 
43878892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
43888892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
438989d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
43908892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
43918892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
43922276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
43932276707eSSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_LEGACY)
43942276707eSSepherosa Ziehau 		ifp->if_npoll = mxge_npoll;
43952276707eSSepherosa Ziehau #endif
439626634ef8SSepherosa Ziehau 	ifp->if_serialize = mxge_serialize;
439726634ef8SSepherosa Ziehau 	ifp->if_deserialize = mxge_deserialize;
439826634ef8SSepherosa Ziehau 	ifp->if_tryserialize = mxge_tryserialize;
439926634ef8SSepherosa Ziehau #ifdef INVARIANTS
440026634ef8SSepherosa Ziehau 	ifp->if_serialize_assert = mxge_serialize_assert;
440126634ef8SSepherosa Ziehau #endif
440289d55360SSepherosa Ziehau 
4403820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4404820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4405820e213fSSepherosa Ziehau 
44068892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
440789d55360SSepherosa Ziehau 	mxge_media_init(sc);
44088892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
440989d55360SSepherosa Ziehau 
4410cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
441189d55360SSepherosa Ziehau 
4412aca8f373SSepherosa Ziehau 	/* Setup TX rings and subqueues */
4413aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
4414aca8f373SSepherosa Ziehau 		struct ifaltq_subque *ifsq = ifq_get_subq(&ifp->if_snd, i);
4415aca8f373SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4416aca8f373SSepherosa Ziehau 
4417aca8f373SSepherosa Ziehau 		ifsq_set_cpuid(ifsq, ss->intr_cpuid);
4418aca8f373SSepherosa Ziehau 		ifsq_set_hw_serialize(ifsq, &ss->tx.tx_serialize);
4419aca8f373SSepherosa Ziehau 		ifsq_set_priv(ifsq, &ss->tx);
4420aca8f373SSepherosa Ziehau 		ss->tx.ifsq = ifsq;
4421aca8f373SSepherosa Ziehau 
4422aca8f373SSepherosa Ziehau 		ifsq_watchdog_init(&ss->tx.watchdog, ifsq, mxge_watchdog);
4423aca8f373SSepherosa Ziehau 	}
4424aca8f373SSepherosa Ziehau 
4425b9a8961fSSepherosa Ziehau 	/*
4426b9a8961fSSepherosa Ziehau 	 * XXX
4427b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4428b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4429b9a8961fSSepherosa Ziehau 	 */
4430b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4431b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
443289d55360SSepherosa Ziehau 	sc->dying = 0;
443389d55360SSepherosa Ziehau 
4434e6c7b753SSepherosa Ziehau 	err = mxge_setup_intr(sc);
4435369c353eSAggelos Economopoulos 	if (err != 0) {
4436798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4437798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4438798c3369SSepherosa Ziehau 		goto failed;
4439369c353eSAggelos Economopoulos 	}
444026634ef8SSepherosa Ziehau 
44418892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
444289d55360SSepherosa Ziehau 
444314929979SSepherosa Ziehau 	/* Increase non-cluster mbuf limit; used by small RX rings */
444414929979SSepherosa Ziehau 	mb_inclimit(ifp->if_nmbclusters);
444514929979SSepherosa Ziehau 
4446c9317e74SSepherosa Ziehau 	callout_reset_bycpu(&sc->co_hdl, mxge_ticks, mxge_tick, sc,
4447e6c7b753SSepherosa Ziehau 	    sc->ss[0].intr_cpuid);
44488892ea20SAggelos Economopoulos 	return 0;
44498892ea20SAggelos Economopoulos 
4450798c3369SSepherosa Ziehau failed:
4451798c3369SSepherosa Ziehau 	mxge_detach(dev);
44528892ea20SAggelos Economopoulos 	return err;
44538892ea20SAggelos Economopoulos }
44548892ea20SAggelos Economopoulos 
44558892ea20SAggelos Economopoulos static int
44568892ea20SAggelos Economopoulos mxge_detach(device_t dev)
44578892ea20SAggelos Economopoulos {
44588892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
44598892ea20SAggelos Economopoulos 
4460798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4461798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
446214929979SSepherosa Ziehau 		int mblimit = ifp->if_nmbclusters;
4463798c3369SSepherosa Ziehau 
446426634ef8SSepherosa Ziehau 		ifnet_serialize_all(ifp);
4465798c3369SSepherosa Ziehau 
44668892ea20SAggelos Economopoulos 		sc->dying = 1;
4467798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
446889d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4469e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4470798c3369SSepherosa Ziehau 
4471e6c7b753SSepherosa Ziehau 		mxge_teardown_intr(sc, sc->num_slices);
4472798c3369SSepherosa Ziehau 
447326634ef8SSepherosa Ziehau 		ifnet_deserialize_all(ifp);
4474e3dc37faSAggelos Economopoulos 
447589d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
447689d55360SSepherosa Ziehau 
4477798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
447814929979SSepherosa Ziehau 
447914929979SSepherosa Ziehau 		/* Decrease non-cluster mbuf limit increased by us */
448014929979SSepherosa Ziehau 		mb_inclimit(-mblimit);
4481798c3369SSepherosa Ziehau 	}
44828892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4483798c3369SSepherosa Ziehau 
4484798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4485798c3369SSepherosa Ziehau 	    sc->sram != NULL)
44868892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4487798c3369SSepherosa Ziehau 
4488e6c7b753SSepherosa Ziehau 	mxge_free_intr(sc);
44898892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
44908892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4491798c3369SSepherosa Ziehau 
4492e6c7b753SSepherosa Ziehau 	/* MUST after sysctls, intr and rings are freed */
44938892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4494798c3369SSepherosa Ziehau 
4495798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
44968892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4497798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
44988892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4499798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
45008892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4501798c3369SSepherosa Ziehau 
4502e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res != NULL) {
4503e6c7b753SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(2),
4504e6c7b753SSepherosa Ziehau 		    sc->msix_table_res);
4505798c3369SSepherosa Ziehau 	}
4506798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4507798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4508798c3369SSepherosa Ziehau 		    sc->mem_res);
4509798c3369SSepherosa Ziehau 	}
4510798c3369SSepherosa Ziehau 
4511798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
45128892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4513798c3369SSepherosa Ziehau 
45148892ea20SAggelos Economopoulos 	return 0;
45158892ea20SAggelos Economopoulos }
45168892ea20SAggelos Economopoulos 
45178892ea20SAggelos Economopoulos static int
45188892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
45198892ea20SAggelos Economopoulos {
45208892ea20SAggelos Economopoulos 	return 0;
45218892ea20SAggelos Economopoulos }
4522e6c7b753SSepherosa Ziehau 
4523e6c7b753SSepherosa Ziehau static void
4524e6c7b753SSepherosa Ziehau mxge_free_msix(struct mxge_softc *sc, boolean_t setup)
4525e6c7b753SSepherosa Ziehau {
4526e6c7b753SSepherosa Ziehau 	int i;
4527e6c7b753SSepherosa Ziehau 
4528e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4529e6c7b753SSepherosa Ziehau 
4530e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4531e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4532e6c7b753SSepherosa Ziehau 
4533e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4534e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4535e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4536e6c7b753SSepherosa Ziehau 		}
4537e6c7b753SSepherosa Ziehau 		if (ss->intr_rid >= 0)
4538e6c7b753SSepherosa Ziehau 			pci_release_msix_vector(sc->dev, ss->intr_rid);
4539e6c7b753SSepherosa Ziehau 	}
4540e6c7b753SSepherosa Ziehau 	if (setup)
4541e6c7b753SSepherosa Ziehau 		pci_teardown_msix(sc->dev);
4542e6c7b753SSepherosa Ziehau }
4543e6c7b753SSepherosa Ziehau 
4544e6c7b753SSepherosa Ziehau static int
4545e6c7b753SSepherosa Ziehau mxge_alloc_msix(struct mxge_softc *sc)
4546e6c7b753SSepherosa Ziehau {
4547e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4548e6c7b753SSepherosa Ziehau 	int offset, rid, error, i;
4549e6c7b753SSepherosa Ziehau 	boolean_t setup = FALSE;
4550e6c7b753SSepherosa Ziehau 
4551e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4552e6c7b753SSepherosa Ziehau 
4553e6c7b753SSepherosa Ziehau 	if (sc->num_slices == ncpus2) {
4554e6c7b753SSepherosa Ziehau 		offset = 0;
4555e6c7b753SSepherosa Ziehau 	} else {
4556e6c7b753SSepherosa Ziehau 		int offset_def;
4557e6c7b753SSepherosa Ziehau 
4558e6c7b753SSepherosa Ziehau 		offset_def = (sc->num_slices * device_get_unit(sc->dev)) %
4559e6c7b753SSepherosa Ziehau 		    ncpus2;
4560e6c7b753SSepherosa Ziehau 
4561e6c7b753SSepherosa Ziehau 		offset = device_getenv_int(sc->dev, "msix.offset", offset_def);
4562e6c7b753SSepherosa Ziehau 		if (offset >= ncpus2 ||
4563e6c7b753SSepherosa Ziehau 		    offset % sc->num_slices != 0) {
4564e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "invalid msix.offset %d, "
4565e6c7b753SSepherosa Ziehau 			    "use %d\n", offset, offset_def);
4566e6c7b753SSepherosa Ziehau 			offset = offset_def;
4567e6c7b753SSepherosa Ziehau 		}
4568e6c7b753SSepherosa Ziehau 	}
4569e6c7b753SSepherosa Ziehau 
4570e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4571e6c7b753SSepherosa Ziehau 
4572e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4573e6c7b753SSepherosa Ziehau 	ss->intr_func = mxge_msi;
4574e6c7b753SSepherosa Ziehau 	ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4575e6c7b753SSepherosa Ziehau 	    "%s comb", device_get_nameunit(sc->dev));
4576e6c7b753SSepherosa Ziehau 	ss->intr_desc = ss->intr_desc0;
4577e6c7b753SSepherosa Ziehau 	ss->intr_cpuid = offset;
4578e6c7b753SSepherosa Ziehau 
4579e6c7b753SSepherosa Ziehau 	for (i = 1; i < sc->num_slices; ++i) {
4580e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4581e6c7b753SSepherosa Ziehau 
4582e6c7b753SSepherosa Ziehau 		ss->intr_serialize = &ss->rx_data.rx_serialize;
4583aca8f373SSepherosa Ziehau 		if (sc->num_tx_rings == 1) {
4584e6c7b753SSepherosa Ziehau 			ss->intr_func = mxge_msix_rx;
4585e6c7b753SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4586e6c7b753SSepherosa Ziehau 			    "%s rx", device_get_nameunit(sc->dev));
4587aca8f373SSepherosa Ziehau 		} else {
4588aca8f373SSepherosa Ziehau 			ss->intr_func = mxge_msix_rxtx;
4589aca8f373SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4590aca8f373SSepherosa Ziehau 			    "%s rxtx", device_get_nameunit(sc->dev));
4591aca8f373SSepherosa Ziehau 		}
4592e6c7b753SSepherosa Ziehau 		ss->intr_desc = ss->intr_desc0;
4593e6c7b753SSepherosa Ziehau 		ss->intr_cpuid = offset + i;
4594e6c7b753SSepherosa Ziehau 	}
4595e6c7b753SSepherosa Ziehau 
4596e6c7b753SSepherosa Ziehau 	rid = PCIR_BAR(2);
4597e6c7b753SSepherosa Ziehau 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
4598e6c7b753SSepherosa Ziehau 	    &rid, RF_ACTIVE);
4599e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res == NULL) {
4600e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "couldn't alloc MSI-X table res\n");
4601e6c7b753SSepherosa Ziehau 		return ENXIO;
4602e6c7b753SSepherosa Ziehau 	}
4603e6c7b753SSepherosa Ziehau 
4604e6c7b753SSepherosa Ziehau 	error = pci_setup_msix(sc->dev);
4605e6c7b753SSepherosa Ziehau 	if (error) {
4606e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not setup MSI-X\n");
4607e6c7b753SSepherosa Ziehau 		goto back;
4608e6c7b753SSepherosa Ziehau 	}
4609e6c7b753SSepherosa Ziehau 	setup = TRUE;
4610e6c7b753SSepherosa Ziehau 
4611e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4612e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4613e6c7b753SSepherosa Ziehau 
4614e6c7b753SSepherosa Ziehau 		error = pci_alloc_msix_vector(sc->dev, i, &ss->intr_rid,
4615e6c7b753SSepherosa Ziehau 		    ss->intr_cpuid);
4616e6c7b753SSepherosa Ziehau 		if (error) {
4617e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4618e6c7b753SSepherosa Ziehau 			    "MSI-X %d on cpu%d\n", i, ss->intr_cpuid);
4619e6c7b753SSepherosa Ziehau 			goto back;
4620e6c7b753SSepherosa Ziehau 		}
4621e6c7b753SSepherosa Ziehau 
4622e6c7b753SSepherosa Ziehau 		ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4623e6c7b753SSepherosa Ziehau 		    &ss->intr_rid, RF_ACTIVE);
4624e6c7b753SSepherosa Ziehau 		if (ss->intr_res == NULL) {
4625e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4626e6c7b753SSepherosa Ziehau 			    "MSI-X %d resource\n", i);
4627e6c7b753SSepherosa Ziehau 			error = ENXIO;
4628e6c7b753SSepherosa Ziehau 			goto back;
4629e6c7b753SSepherosa Ziehau 		}
4630e6c7b753SSepherosa Ziehau 	}
4631e6c7b753SSepherosa Ziehau 
4632e6c7b753SSepherosa Ziehau 	pci_enable_msix(sc->dev);
4633e6c7b753SSepherosa Ziehau 	sc->intr_type = PCI_INTR_TYPE_MSIX;
4634e6c7b753SSepherosa Ziehau back:
4635e6c7b753SSepherosa Ziehau 	if (error)
4636e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, setup);
4637e6c7b753SSepherosa Ziehau 	return error;
4638e6c7b753SSepherosa Ziehau }
4639e6c7b753SSepherosa Ziehau 
4640e6c7b753SSepherosa Ziehau static int
4641e6c7b753SSepherosa Ziehau mxge_alloc_intr(struct mxge_softc *sc)
4642e6c7b753SSepherosa Ziehau {
4643e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4644e6c7b753SSepherosa Ziehau 	u_int irq_flags;
4645e6c7b753SSepherosa Ziehau 
4646e6c7b753SSepherosa Ziehau 	if (sc->num_slices > 1) {
4647e6c7b753SSepherosa Ziehau 		int error;
4648e6c7b753SSepherosa Ziehau 
4649e6c7b753SSepherosa Ziehau 		error = mxge_alloc_msix(sc);
4650e6c7b753SSepherosa Ziehau 		if (error)
4651e6c7b753SSepherosa Ziehau 			return error;
4652e6c7b753SSepherosa Ziehau 		KKASSERT(sc->intr_type == PCI_INTR_TYPE_MSIX);
4653e6c7b753SSepherosa Ziehau 		return 0;
4654e6c7b753SSepherosa Ziehau 	}
4655e6c7b753SSepherosa Ziehau 
4656e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4657e6c7b753SSepherosa Ziehau 
4658e6c7b753SSepherosa Ziehau 	sc->intr_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
4659e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, &irq_flags);
4660e6c7b753SSepherosa Ziehau 
4661e6c7b753SSepherosa Ziehau 	ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4662e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, irq_flags);
4663e6c7b753SSepherosa Ziehau 	if (ss->intr_res == NULL) {
4664e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not alloc interrupt\n");
4665e6c7b753SSepherosa Ziehau 		return ENXIO;
4666e6c7b753SSepherosa Ziehau 	}
4667e6c7b753SSepherosa Ziehau 
4668e6c7b753SSepherosa Ziehau 	if (sc->intr_type == PCI_INTR_TYPE_LEGACY)
4669e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_legacy;
4670e6c7b753SSepherosa Ziehau 	else
4671e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_msi;
4672e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4673e6c7b753SSepherosa Ziehau 	ss->intr_cpuid = rman_get_cpuid(ss->intr_res);
4674e6c7b753SSepherosa Ziehau 
4675e6c7b753SSepherosa Ziehau 	return 0;
4676e6c7b753SSepherosa Ziehau }
4677e6c7b753SSepherosa Ziehau 
4678e6c7b753SSepherosa Ziehau static int
4679e6c7b753SSepherosa Ziehau mxge_setup_intr(struct mxge_softc *sc)
4680e6c7b753SSepherosa Ziehau {
4681e6c7b753SSepherosa Ziehau 	int i;
4682e6c7b753SSepherosa Ziehau 
4683e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4684e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4685e6c7b753SSepherosa Ziehau 		int error;
4686e6c7b753SSepherosa Ziehau 
4687e6c7b753SSepherosa Ziehau 		error = bus_setup_intr_descr(sc->dev, ss->intr_res,
4688e6c7b753SSepherosa Ziehau 		    INTR_MPSAFE, ss->intr_func, ss, &ss->intr_hand,
4689e6c7b753SSepherosa Ziehau 		    ss->intr_serialize, ss->intr_desc);
4690e6c7b753SSepherosa Ziehau 		if (error) {
4691e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "can't setup %dth intr\n", i);
4692e6c7b753SSepherosa Ziehau 			mxge_teardown_intr(sc, i);
4693e6c7b753SSepherosa Ziehau 			return error;
4694e6c7b753SSepherosa Ziehau 		}
4695e6c7b753SSepherosa Ziehau 	}
4696e6c7b753SSepherosa Ziehau 	return 0;
4697e6c7b753SSepherosa Ziehau }
4698e6c7b753SSepherosa Ziehau 
4699e6c7b753SSepherosa Ziehau static void
4700e6c7b753SSepherosa Ziehau mxge_teardown_intr(struct mxge_softc *sc, int cnt)
4701e6c7b753SSepherosa Ziehau {
4702e6c7b753SSepherosa Ziehau 	int i;
4703e6c7b753SSepherosa Ziehau 
4704e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4705e6c7b753SSepherosa Ziehau 		return;
4706e6c7b753SSepherosa Ziehau 
4707e6c7b753SSepherosa Ziehau 	for (i = 0; i < cnt; ++i) {
4708e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4709e6c7b753SSepherosa Ziehau 
4710e6c7b753SSepherosa Ziehau 		bus_teardown_intr(sc->dev, ss->intr_res, ss->intr_hand);
4711e6c7b753SSepherosa Ziehau 	}
4712e6c7b753SSepherosa Ziehau }
4713e6c7b753SSepherosa Ziehau 
4714e6c7b753SSepherosa Ziehau static void
4715e6c7b753SSepherosa Ziehau mxge_free_intr(struct mxge_softc *sc)
4716e6c7b753SSepherosa Ziehau {
4717e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4718e6c7b753SSepherosa Ziehau 		return;
4719e6c7b753SSepherosa Ziehau 
4720e6c7b753SSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_MSIX) {
4721e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[0];
4722e6c7b753SSepherosa Ziehau 
4723e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4724e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4725e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4726e6c7b753SSepherosa Ziehau 		}
4727e6c7b753SSepherosa Ziehau 		if (sc->intr_type == PCI_INTR_TYPE_MSI)
4728e6c7b753SSepherosa Ziehau 			pci_release_msi(sc->dev);
4729e6c7b753SSepherosa Ziehau 	} else {
4730e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, TRUE);
4731e6c7b753SSepherosa Ziehau 	}
4732e6c7b753SSepherosa Ziehau }
4733