xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision bdbc20ad)
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 
8070f95ad1SSascha Wildner #if 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 {
19970f95ad1SSascha Wildner #if 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 
29570f95ad1SSascha Wildner #if 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 
41970f95ad1SSascha Wildner #else	/* __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 
100457e09377SMatthew 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 
102357e09377SMatthew 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*/
104857e09377SMatthew 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 
1430*bdbc20adSSepherosa Ziehau 	if (sc->num_slices > 1) {
1431*bdbc20adSSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "slice_cpumap",
1432*bdbc20adSSepherosa Ziehau 		    CTLTYPE_OPAQUE | CTLFLAG_RD, sc->ring_map, 0,
1433*bdbc20adSSepherosa Ziehau 		    if_ringmap_cpumap_sysctl, "I", "slice CPU map");
1434*bdbc20adSSepherosa Ziehau 	}
1435*bdbc20adSSepherosa Ziehau 
14367cc92483SSepherosa Ziehau 	/*
14377cc92483SSepherosa Ziehau 	 * Performance related tunables
14387cc92483SSepherosa Ziehau 	 */
14397cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14407cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14417cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14427cc92483SSepherosa Ziehau 
14437cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14447cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14457cc92483SSepherosa Ziehau 	    "Transmit throttling");
14467cc92483SSepherosa Ziehau 
14478433e5f5SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "use_rss",
14488433e5f5SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_use_rss, "I",
14498433e5f5SSepherosa Ziehau 	    "Use RSS");
14508433e5f5SSepherosa Ziehau 
14517cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14527cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14537cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14547cc92483SSepherosa Ziehau 
14557cc92483SSepherosa Ziehau 	/*
14567cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14577cc92483SSepherosa Ziehau 	 * Need to swap it
14587cc92483SSepherosa Ziehau 	 */
14597cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14607cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14617cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14627cc92483SSepherosa Ziehau 
14637cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14647cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14657cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14667cc92483SSepherosa Ziehau 
14677cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14687cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14697cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14707cc92483SSepherosa Ziehau 
14717cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14727cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14737cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14747cc92483SSepherosa Ziehau 
14757cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14767cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14777cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14787cc92483SSepherosa Ziehau 
14797cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14807cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14817cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14827cc92483SSepherosa Ziehau 
14837cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14847cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14857cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14867cc92483SSepherosa Ziehau 
14877cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14887cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14897cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14907cc92483SSepherosa Ziehau 
14917cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14927cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14937cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14947cc92483SSepherosa Ziehau 
14957cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14967cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14977cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14987cc92483SSepherosa Ziehau 
14997cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
15007cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
15017cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
15027cc92483SSepherosa Ziehau 
15037cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
15047cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
15057cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
15067cc92483SSepherosa Ziehau 
15077cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
15087cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
15097cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
15108892ea20SAggelos Economopoulos 
15118892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
15128892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15137cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
15147cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1515798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1516798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1517798c3369SSepherosa Ziehau 		return;
1518798c3369SSepherosa Ziehau 	}
15198892ea20SAggelos Economopoulos 
15208892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
15218892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
15228892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
15238892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
15248892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1525b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
15267cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15277cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1528798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1529798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1530798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1531798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1532798c3369SSepherosa Ziehau 		}
15338892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15347cc92483SSepherosa Ziehau 
15357cc92483SSepherosa Ziehau 		/*
15367cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15377cc92483SSepherosa Ziehau 		 */
15387cc92483SSepherosa Ziehau 
15397cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15409a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_small.cnt, 0, "rx_small_cnt");
15417cc92483SSepherosa Ziehau 
15427cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15439a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_big.cnt, 0, "rx_small_cnt");
15448892ea20SAggelos Economopoulos 
15457cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15467cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15477cc92483SSepherosa Ziehau 
15487cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15497cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15507cc92483SSepherosa Ziehau 
15517cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15527cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15537cc92483SSepherosa Ziehau 
15547cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15557cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15567cc92483SSepherosa Ziehau 
15577cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15587cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15597cc92483SSepherosa Ziehau 
15607cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15617cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15628892ea20SAggelos Economopoulos 	}
15638892ea20SAggelos Economopoulos }
15648892ea20SAggelos Economopoulos 
156589d55360SSepherosa Ziehau /*
156689d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
156789d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
156889d55360SSepherosa Ziehau  */
1569ddbf91b7SSepherosa Ziehau static __inline void
15708892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15718892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15728892ea20SAggelos Economopoulos {
15738892ea20SAggelos Economopoulos 	int idx, starting_slot;
15745ca32f31SSepherosa Ziehau 
15758892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15768892ea20SAggelos Economopoulos 	while (cnt > 1) {
15778892ea20SAggelos Economopoulos 		cnt--;
15788892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15795ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15808892ea20SAggelos Economopoulos 		wmb();
15818892ea20SAggelos Economopoulos 	}
15828892ea20SAggelos Economopoulos }
15838892ea20SAggelos Economopoulos 
15848892ea20SAggelos Economopoulos /*
158589d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15868892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15878892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15888892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15898892ea20SAggelos Economopoulos  */
1590ddbf91b7SSepherosa Ziehau static __inline void
159189d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15928892ea20SAggelos Economopoulos {
15938892ea20SAggelos Economopoulos 	int idx, i;
15948892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15958892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15968892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15978892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15988892ea20SAggelos Economopoulos 	uint8_t last_flags;
15998892ea20SAggelos Economopoulos 
16008892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
16018892ea20SAggelos Economopoulos 
16028892ea20SAggelos Economopoulos 	last_flags = src->flags;
16038892ea20SAggelos Economopoulos 	src->flags = 0;
16048892ea20SAggelos Economopoulos 	wmb();
16058892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
16068892ea20SAggelos Economopoulos 	srcp = src;
16078892ea20SAggelos Economopoulos 
16088892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
16095ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
16108892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
16118892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
16128892ea20SAggelos Economopoulos 			srcp += 2;
16138892ea20SAggelos Economopoulos 			dstp += 2;
16148892ea20SAggelos Economopoulos 		}
16158892ea20SAggelos Economopoulos 	} else {
16165ca32f31SSepherosa Ziehau 		/*
16175ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
16185ca32f31SSepherosa Ziehau 		 * that it is submitted below
16195ca32f31SSepherosa Ziehau 		 */
16208892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16218892ea20SAggelos Economopoulos 		i = 0;
16228892ea20SAggelos Economopoulos 	}
16238892ea20SAggelos Economopoulos 	if (i < cnt) {
16245ca32f31SSepherosa Ziehau 		/* Submit the first request */
16258892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16268892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16278892ea20SAggelos Economopoulos 	}
16288892ea20SAggelos Economopoulos 
16295ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16308892ea20SAggelos Economopoulos 	src->flags = last_flags;
16318892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16328892ea20SAggelos Economopoulos 	src_ints+=3;
16338892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16348892ea20SAggelos Economopoulos 	dst_ints+=3;
16358892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16368892ea20SAggelos Economopoulos 	tx->req += cnt;
16378892ea20SAggelos Economopoulos 	wmb();
16388892ea20SAggelos Economopoulos }
16398892ea20SAggelos Economopoulos 
164089d55360SSepherosa Ziehau static int
164189d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
164289d55360SSepherosa Ziehau {
164389d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
164489d55360SSepherosa Ziehau 	struct mbuf *m;
164589d55360SSepherosa Ziehau 
164689d55360SSepherosa Ziehau 	m = *mp;
164789d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
164889d55360SSepherosa Ziehau 
164989d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
165089d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
165189d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
165289d55360SSepherosa Ziehau 
165389d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
165489d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
165589d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
165689d55360SSepherosa Ziehau 
165789d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
165889d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
165989d55360SSepherosa Ziehau 		if (m == NULL) {
166089d55360SSepherosa Ziehau 			*mp = NULL;
166189d55360SSepherosa Ziehau 			return ENOBUFS;
166289d55360SSepherosa Ziehau 		}
166389d55360SSepherosa Ziehau 		*mp = m;
166489d55360SSepherosa Ziehau 	}
166589d55360SSepherosa Ziehau 	return 0;
166689d55360SSepherosa Ziehau }
16678892ea20SAggelos Economopoulos 
1668ca8ca004SSepherosa Ziehau static int
1669fb39a573SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mxge_buffer_state *info_map,
167048d12a0bSSepherosa Ziehau     struct mbuf *m, int busdma_seg_cnt)
16718892ea20SAggelos Economopoulos {
16728892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16738892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16748892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16758892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16768892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16778892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16788892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
1679fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_last;
168048d12a0bSSepherosa Ziehau 	bus_dmamap_t map = info_map->map;
16818892ea20SAggelos Economopoulos 
16828892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16838892ea20SAggelos Economopoulos 
16845ca32f31SSepherosa Ziehau 	/*
16855ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16865ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16878892ea20SAggelos Economopoulos 	 */
168889d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
168989d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16908892ea20SAggelos Economopoulos 
16915ca32f31SSepherosa Ziehau 	/*
16925ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16935ca32f31SSepherosa Ziehau 	 */
169489d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16958892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16968892ea20SAggelos Economopoulos 
16975ca32f31SSepherosa Ziehau 	/*
16985ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
16995ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
17005ca32f31SSepherosa Ziehau 	 */
17018892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
17028892ea20SAggelos Economopoulos 
17038892ea20SAggelos Economopoulos 	req = tx->req_list;
17048892ea20SAggelos Economopoulos 	seg = tx->seg_list;
17058892ea20SAggelos Economopoulos 	cnt = 0;
17068892ea20SAggelos Economopoulos 	rdma_count = 0;
17075ca32f31SSepherosa Ziehau 
17085ca32f31SSepherosa Ziehau 	/*
17095ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
17105ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
17115ca32f31SSepherosa Ziehau 	 * this is equal to "count".
17128892ea20SAggelos Economopoulos 	 *
17135ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
17145ca32f31SSepherosa Ziehau 	 * segment cut.
17158892ea20SAggelos Economopoulos 	 *
17165ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
17175ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
17185ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
17195ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
17205ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
17215ca32f31SSepherosa Ziehau 	 * RDMA.
17225ca32f31SSepherosa Ziehau 	 *
17235ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
17245ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17255ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17268892ea20SAggelos Economopoulos 	 */
17278892ea20SAggelos Economopoulos 
17288892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17295ca32f31SSepherosa Ziehau 		/*
17305ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17315ca32f31SSepherosa Ziehau 		 */
17328892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17338892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17348892ea20SAggelos Economopoulos 		len = seg->ds_len;
17358892ea20SAggelos Economopoulos 
17368892ea20SAggelos Economopoulos 		while (len) {
17378892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17388892ea20SAggelos Economopoulos 			seglen = len;
17398892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17408892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17418892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17425ca32f31SSepherosa Ziehau 				/* Payload */
17438892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17448892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17458892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17468892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17475ca32f31SSepherosa Ziehau 				flags_next |=
17485ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17498892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17508892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17518892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17525ca32f31SSepherosa Ziehau 				/* Header ends */
17538892ea20SAggelos Economopoulos 				rdma_count = -1;
17548892ea20SAggelos Economopoulos 				cum_len_next = 0;
17558892ea20SAggelos Economopoulos 				seglen = -cum_len;
17568892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17578892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17588892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17598892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17608892ea20SAggelos Economopoulos 			}
17618892ea20SAggelos Economopoulos 
17628892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17638892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17648892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17658892ea20SAggelos Economopoulos 			req->pad = 0;
17668892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17678892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17688892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17695ca32f31SSepherosa Ziehau 			req->flags =
17705ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17718892ea20SAggelos Economopoulos 			low += seglen;
17728892ea20SAggelos Economopoulos 			len -= seglen;
17738892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17748892ea20SAggelos Economopoulos 			flags = flags_next;
17758892ea20SAggelos Economopoulos 			req++;
17768892ea20SAggelos Economopoulos 			cnt++;
17778892ea20SAggelos Economopoulos 			rdma_count++;
17788892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17798892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17808892ea20SAggelos Economopoulos 			else
17818892ea20SAggelos Economopoulos 				cksum_offset = 0;
17828892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17838892ea20SAggelos Economopoulos 				goto drop;
17848892ea20SAggelos Economopoulos 		}
17858892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17868892ea20SAggelos Economopoulos 		seg++;
17878892ea20SAggelos Economopoulos 	}
17888892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17898892ea20SAggelos Economopoulos 
17908892ea20SAggelos Economopoulos 	do {
17918892ea20SAggelos Economopoulos 		req--;
17928892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17938892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17948892ea20SAggelos Economopoulos 
179548d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
179648d12a0bSSepherosa Ziehau 
179748d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
179848d12a0bSSepherosa Ziehau 	info_last->map = map;
179948d12a0bSSepherosa Ziehau 	info_last->m = m;
180048d12a0bSSepherosa Ziehau 
18018892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
18020654769eSSepherosa Ziehau 
1803aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1804aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1805aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1806aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1807aca8f373SSepherosa Ziehau 		tx->activate++;
1808aca8f373SSepherosa Ziehau 		wmb();
1809aca8f373SSepherosa Ziehau 	}
1810ca8ca004SSepherosa Ziehau 	return 0;
18118892ea20SAggelos Economopoulos 
18128892ea20SAggelos Economopoulos drop:
18138892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
18148892ea20SAggelos Economopoulos 	m_freem(m);
1815ca8ca004SSepherosa Ziehau 	return ENOBUFS;
18168892ea20SAggelos Economopoulos }
18178892ea20SAggelos Economopoulos 
1818ca8ca004SSepherosa Ziehau static int
18195da1e9c3SSepherosa Ziehau mxge_encap(mxge_tx_ring_t *tx, struct mbuf *m, bus_addr_t zeropad)
18208892ea20SAggelos Economopoulos {
18218892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
18228892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
182348d12a0bSSepherosa Ziehau 	bus_dmamap_t map;
182489d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
18258892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
18268892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
1827fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_map, *info_last;
18288892ea20SAggelos Economopoulos 
182989d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1830ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1831ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1832ca8ca004SSepherosa Ziehau 			return err;
18338892ea20SAggelos Economopoulos 	}
183489d55360SSepherosa Ziehau 
18355ca32f31SSepherosa Ziehau 	/*
18365ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18375ca32f31SSepherosa Ziehau 	 */
183889d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
183948d12a0bSSepherosa Ziehau 	info_map = &tx->info[idx];
184048d12a0bSSepherosa Ziehau 	map = info_map->map;
184148d12a0bSSepherosa Ziehau 
184248d12a0bSSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, map, &m,
184389d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
184489d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
184589d55360SSepherosa Ziehau 		goto drop;
184648d12a0bSSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, map, BUS_DMASYNC_PREWRITE);
184789d55360SSepherosa Ziehau 
18485ca32f31SSepherosa Ziehau 	/*
18495ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18505ca32f31SSepherosa Ziehau 	 */
1851ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
185248d12a0bSSepherosa Ziehau 		return mxge_encap_tso(tx, info_map, m, cnt);
18538892ea20SAggelos Economopoulos 
18548892ea20SAggelos Economopoulos 	req = tx->req_list;
18558892ea20SAggelos Economopoulos 	cksum_offset = 0;
18568892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18578892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18588892ea20SAggelos Economopoulos 
18595ca32f31SSepherosa Ziehau 	/*
18605ca32f31SSepherosa Ziehau 	 * Checksum offloading
18615ca32f31SSepherosa Ziehau 	 */
186289d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
186389d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18648892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18658892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18668892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18678892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18688892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18698892ea20SAggelos Economopoulos 	} else {
18708892ea20SAggelos Economopoulos 		odd_flag = 0;
18718892ea20SAggelos Economopoulos 	}
18728892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18738892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18748892ea20SAggelos Economopoulos 
18755ca32f31SSepherosa Ziehau 	/*
18765ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18775ca32f31SSepherosa Ziehau 	 */
18788892ea20SAggelos Economopoulos 	cum_len = 0;
18798892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18808892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18818892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18825ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18835ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18848892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18858892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18868892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18878892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18888892ea20SAggelos Economopoulos 		else
18898892ea20SAggelos Economopoulos 			cksum_offset = 0;
18908892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18918892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18928892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18938892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18948892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18958892ea20SAggelos Economopoulos 		seg++;
18968892ea20SAggelos Economopoulos 		req++;
18978892ea20SAggelos Economopoulos 		req->flags = 0;
18988892ea20SAggelos Economopoulos 	}
18998892ea20SAggelos Economopoulos 	req--;
19005ca32f31SSepherosa Ziehau 
19015ca32f31SSepherosa Ziehau 	/*
19025ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
19035ca32f31SSepherosa Ziehau 	 */
19048892ea20SAggelos Economopoulos 	if (cum_len < 60) {
19058892ea20SAggelos Economopoulos 		req++;
19065da1e9c3SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(zeropad));
19075da1e9c3SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(zeropad));
19088892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
19098892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
19108892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
19118892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
19128892ea20SAggelos Economopoulos 		req->rdma_count = 1;
19138892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
19148892ea20SAggelos Economopoulos 		cnt++;
19158892ea20SAggelos Economopoulos 	}
19168892ea20SAggelos Economopoulos 
19178892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
19188892ea20SAggelos Economopoulos #if 0
19198892ea20SAggelos Economopoulos 	/* print what the firmware will see */
19208892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
19216c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
19228892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
19238892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19248892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19258892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19268892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19278892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19288892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19298892ea20SAggelos Economopoulos 	}
19306c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19318892ea20SAggelos Economopoulos #endif
193248d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
193348d12a0bSSepherosa Ziehau 
193448d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
193548d12a0bSSepherosa Ziehau 	info_last->map = map;
193648d12a0bSSepherosa Ziehau 	info_last->m = m;
193748d12a0bSSepherosa Ziehau 
19388892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19390654769eSSepherosa Ziehau 
1940aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1941aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1942aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1943aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1944aca8f373SSepherosa Ziehau 		tx->activate++;
1945aca8f373SSepherosa Ziehau 		wmb();
1946aca8f373SSepherosa Ziehau 	}
1947ca8ca004SSepherosa Ziehau 	return 0;
19488892ea20SAggelos Economopoulos 
19498892ea20SAggelos Economopoulos drop:
19508892ea20SAggelos Economopoulos 	m_freem(m);
1951ca8ca004SSepherosa Ziehau 	return err;
19528892ea20SAggelos Economopoulos }
19538892ea20SAggelos Economopoulos 
19548892ea20SAggelos Economopoulos static void
1955f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19568892ea20SAggelos Economopoulos {
19578892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
1958aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
19595da1e9c3SSepherosa Ziehau 	bus_addr_t zeropad;
1960ca8ca004SSepherosa Ziehau 	int encap = 0;
19618892ea20SAggelos Economopoulos 
1962aca8f373SSepherosa Ziehau 	KKASSERT(tx->ifsq == ifsq);
196326634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
1964795c96bbSSepherosa Ziehau 
1965795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1966795c96bbSSepherosa Ziehau 		return;
1967795c96bbSSepherosa Ziehau 
19685da1e9c3SSepherosa Ziehau 	zeropad = sc->zeropad_dma.dmem_busaddr;
1969795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1970795c96bbSSepherosa Ziehau 		struct mbuf *m;
1971ca8ca004SSepherosa Ziehau 		int error;
1972795c96bbSSepherosa Ziehau 
1973795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1974795c96bbSSepherosa Ziehau 		if (m == NULL)
1975ca8ca004SSepherosa Ziehau 			goto done;
1976795c96bbSSepherosa Ziehau 
1977795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
19785da1e9c3SSepherosa Ziehau 		error = mxge_encap(tx, m, zeropad);
1979ca8ca004SSepherosa Ziehau 		if (!error)
1980ca8ca004SSepherosa Ziehau 			encap = 1;
1981fed54363SSepherosa Ziehau 		else
1982fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1983795c96bbSSepherosa Ziehau 	}
1984795c96bbSSepherosa Ziehau 
1985795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1986795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1987ca8ca004SSepherosa Ziehau done:
1988ca8ca004SSepherosa Ziehau 	if (encap)
1989aca8f373SSepherosa Ziehau 		tx->watchdog.wd_timer = 5;
1990ca8ca004SSepherosa Ziehau }
1991ca8ca004SSepherosa Ziehau 
1992ca8ca004SSepherosa Ziehau static void
1993aca8f373SSepherosa Ziehau mxge_watchdog(struct ifaltq_subque *ifsq)
1994ca8ca004SSepherosa Ziehau {
1995aca8f373SSepherosa Ziehau 	struct ifnet *ifp = ifsq_get_ifp(ifsq);
1996ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1997ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1998aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
1999ca8ca004SSepherosa Ziehau 
200026634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
2001ca8ca004SSepherosa Ziehau 
2002ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
2003ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
2004ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
2005ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
2006ca8ca004SSepherosa Ziehau 		return;
2007ca8ca004SSepherosa Ziehau 	} else {
2008ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
2009ca8ca004SSepherosa Ziehau 		    "check link partner\n");
2010ca8ca004SSepherosa Ziehau 	}
2011ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
20128892ea20SAggelos Economopoulos }
20138892ea20SAggelos Economopoulos 
20148892ea20SAggelos Economopoulos /*
20152f47b54fSSepherosa Ziehau  * Copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
20168892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
20178892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's low
20188892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
20198892ea20SAggelos Economopoulos  * in a burst
20208892ea20SAggelos Economopoulos  */
2021ddbf91b7SSepherosa Ziehau static __inline void
20228892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
20238892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
20248892ea20SAggelos Economopoulos {
20258892ea20SAggelos Economopoulos 	uint32_t low;
20268892ea20SAggelos Economopoulos 
20278892ea20SAggelos Economopoulos 	low = src->addr_low;
20288892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
20298892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
20308892ea20SAggelos Economopoulos 	wmb();
20318892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
20328892ea20SAggelos Economopoulos 	wmb();
20338892ea20SAggelos Economopoulos 	src->addr_low = low;
20348892ea20SAggelos Economopoulos 	dst->addr_low = low;
20358892ea20SAggelos Economopoulos 	wmb();
20368892ea20SAggelos Economopoulos }
20378892ea20SAggelos Economopoulos 
20388892ea20SAggelos Economopoulos static int
20398ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20408ebf015eSSepherosa Ziehau     boolean_t init)
20418892ea20SAggelos Economopoulos {
20428892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20438892ea20SAggelos Economopoulos 	struct mbuf *m;
2044363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20458892ea20SAggelos Economopoulos 
2046b5523eacSSascha Wildner 	mflag = M_NOWAIT;
20478ebf015eSSepherosa Ziehau 	if (__predict_false(init))
2048b5523eacSSascha Wildner 		mflag = M_WAITOK;
20498ebf015eSSepherosa Ziehau 
20508ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20518892ea20SAggelos Economopoulos 	if (m == NULL) {
20528892ea20SAggelos Economopoulos 		err = ENOBUFS;
20538ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20548ebf015eSSepherosa Ziehau 			/*
20558ebf015eSSepherosa Ziehau 			 * During initialization, there
20568ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20578ebf015eSSepherosa Ziehau 			 */
20588ebf015eSSepherosa Ziehau 			return err;
20598ebf015eSSepherosa Ziehau 		}
20608892ea20SAggelos Economopoulos 		goto done;
20618892ea20SAggelos Economopoulos 	}
20622823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20638ebf015eSSepherosa Ziehau 
20647d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20657d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20668892ea20SAggelos Economopoulos 	if (err != 0) {
20678ebf015eSSepherosa Ziehau 		m_freem(m);
20688ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20698ebf015eSSepherosa Ziehau 			/*
20708ebf015eSSepherosa Ziehau 			 * During initialization, there
20718ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20728ebf015eSSepherosa Ziehau 			 */
20738ebf015eSSepherosa Ziehau 			return err;
20748ebf015eSSepherosa Ziehau 		}
20758892ea20SAggelos Economopoulos 		goto done;
20768892ea20SAggelos Economopoulos 	}
20778ebf015eSSepherosa Ziehau 
20788892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20798ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20808ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20818892ea20SAggelos Economopoulos 
20828892ea20SAggelos Economopoulos done:
20838892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20848892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20858892ea20SAggelos Economopoulos 	return err;
20868892ea20SAggelos Economopoulos }
20878892ea20SAggelos Economopoulos 
20888892ea20SAggelos Economopoulos static int
2089363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2090363b44f8SSepherosa Ziehau     boolean_t init)
20918892ea20SAggelos Economopoulos {
2092b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20938892ea20SAggelos Economopoulos 	struct mbuf *m;
2094363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2095363b44f8SSepherosa Ziehau 
2096b5523eacSSascha Wildner 	mflag = M_NOWAIT;
2097363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2098b5523eacSSascha Wildner 		mflag = M_WAITOK;
20998892ea20SAggelos Economopoulos 
21008892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2101363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2102b9a8961fSSepherosa Ziehau 	else
2103363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
21048892ea20SAggelos Economopoulos 	if (m == NULL) {
21058892ea20SAggelos Economopoulos 		err = ENOBUFS;
2106363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2107363b44f8SSepherosa Ziehau 			/*
2108363b44f8SSepherosa Ziehau 			 * During initialization, there
2109363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2110363b44f8SSepherosa Ziehau 			 */
2111363b44f8SSepherosa Ziehau 			return err;
2112363b44f8SSepherosa Ziehau 		}
21138892ea20SAggelos Economopoulos 		goto done;
21148892ea20SAggelos Economopoulos 	}
21153ae50fbeSSepherosa Ziehau 	m->m_len = m->m_pkthdr.len = rx->cl_size;
2116b9a8961fSSepherosa Ziehau 
21177d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2118b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
21198892ea20SAggelos Economopoulos 	if (err != 0) {
2120363b44f8SSepherosa Ziehau 		m_freem(m);
2121363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2122363b44f8SSepherosa Ziehau 			/*
2123363b44f8SSepherosa Ziehau 			 * During initialization, there
2124363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2125363b44f8SSepherosa Ziehau 			 */
2126363b44f8SSepherosa Ziehau 			return err;
2127363b44f8SSepherosa Ziehau 		}
21288892ea20SAggelos Economopoulos 		goto done;
21298892ea20SAggelos Economopoulos 	}
2130b9a8961fSSepherosa Ziehau 
21318892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2132363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2133363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21348892ea20SAggelos Economopoulos 
21358892ea20SAggelos Economopoulos done:
2136b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2137b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21388892ea20SAggelos Economopoulos 	return err;
21398892ea20SAggelos Economopoulos }
21408892ea20SAggelos Economopoulos 
21418892ea20SAggelos Economopoulos /*
21428892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21438892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21448892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21458892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21468892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21478892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21488892ea20SAggelos Economopoulos  */
214952cf8dfcSSepherosa Ziehau static __inline uint16_t
21508892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21518892ea20SAggelos Economopoulos {
215252cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
215352cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21548892ea20SAggelos Economopoulos 	uint16_t c;
21558892ea20SAggelos Economopoulos 
215652cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21578892ea20SAggelos Economopoulos 
215852cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21598892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21608892ea20SAggelos Economopoulos 		return 1;
216152cf8dfcSSepherosa Ziehau 
216252cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
216352cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21648892ea20SAggelos Economopoulos 		return 1;
216552cf8dfcSSepherosa Ziehau 
21668892ea20SAggelos Economopoulos #ifdef INET
21678892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21688892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21698892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21708892ea20SAggelos Economopoulos #else
21718892ea20SAggelos Economopoulos 	c = 1;
21728892ea20SAggelos Economopoulos #endif
21738892ea20SAggelos Economopoulos 	c ^= 0xffff;
217452cf8dfcSSepherosa Ziehau 	return c;
21758892ea20SAggelos Economopoulos }
21768892ea20SAggelos Economopoulos 
21778892ea20SAggelos Economopoulos static void
21788892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21798892ea20SAggelos Economopoulos {
21808892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21818892ea20SAggelos Economopoulos 	uint32_t partial;
21828892ea20SAggelos Economopoulos 
21838892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21848892ea20SAggelos Economopoulos 
21858892ea20SAggelos Economopoulos 	/*
218652cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
218752cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21888892ea20SAggelos Economopoulos 	 * header.
21898892ea20SAggelos Economopoulos 	 */
21908892ea20SAggelos Economopoulos 
219152cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21928892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21938892ea20SAggelos Economopoulos 
219452cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
219552cf8dfcSSepherosa Ziehau 	*csum += ~partial;
219652cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
219752cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
219852cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
219952cf8dfcSSepherosa Ziehau 
220052cf8dfcSSepherosa Ziehau 	/*
220152cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
220252cf8dfcSSepherosa Ziehau 	 * later consumers expect this
220352cf8dfcSSepherosa Ziehau 	 */
22048892ea20SAggelos Economopoulos 	*csum = htons(*csum);
22058892ea20SAggelos Economopoulos 
22068892ea20SAggelos Economopoulos 	/* save the tag */
2207b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
22088892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
22098892ea20SAggelos Economopoulos 
22108892ea20SAggelos Economopoulos 	/*
22118892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
22128892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
22138892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
22148892ea20SAggelos Economopoulos 	 * type field is already in place.
22158892ea20SAggelos Economopoulos 	 */
2216b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
22178892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2218b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
22198892ea20SAggelos Economopoulos }
22208892ea20SAggelos Economopoulos 
22218892ea20SAggelos Economopoulos 
222252cf8dfcSSepherosa Ziehau static __inline void
22235da1e9c3SSepherosa Ziehau mxge_rx_done_big(struct ifnet *ifp, mxge_rx_ring_t *rx,
22245da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22258892ea20SAggelos Economopoulos {
22268892ea20SAggelos Economopoulos 	struct mbuf *m;
222752cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22288892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22298892ea20SAggelos Economopoulos 	int idx;
22308892ea20SAggelos Economopoulos 
22318892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2232b9a8961fSSepherosa Ziehau 	rx->cnt++;
223352cf8dfcSSepherosa Ziehau 
223452cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22358892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
223652cf8dfcSSepherosa Ziehau 
223752cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2238363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
223952cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2240d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22418892ea20SAggelos Economopoulos 		return;
22428892ea20SAggelos Economopoulos 	}
22438892ea20SAggelos Economopoulos 
224452cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22458892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22468892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22478892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22488892ea20SAggelos Economopoulos 
224952cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22508892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22518892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22528892ea20SAggelos Economopoulos 
225352cf8dfcSSepherosa Ziehau 	/*
225452cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
225552cf8dfcSSepherosa Ziehau 	 * aligned
225652cf8dfcSSepherosa Ziehau 	 */
22578892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22588892ea20SAggelos Economopoulos 
22598892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22608892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
226152cf8dfcSSepherosa Ziehau 
2262cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
226352cf8dfcSSepherosa Ziehau 
226452cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
226552cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22668892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
226752cf8dfcSSepherosa Ziehau 
226852cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
226989d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
227052cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
227189d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22728892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
227389d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
227489d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22758892ea20SAggelos Economopoulos 	}
227673029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
22778892ea20SAggelos Economopoulos }
22788892ea20SAggelos Economopoulos 
227952cf8dfcSSepherosa Ziehau static __inline void
22805da1e9c3SSepherosa Ziehau mxge_rx_done_small(struct ifnet *ifp, mxge_rx_ring_t *rx,
22815da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22828892ea20SAggelos Economopoulos {
228352cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22848892ea20SAggelos Economopoulos 	struct mbuf *m;
22858892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22868892ea20SAggelos Economopoulos 	int idx;
22878892ea20SAggelos Economopoulos 
22888892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22898892ea20SAggelos Economopoulos 	rx->cnt++;
229052cf8dfcSSepherosa Ziehau 
229152cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22928892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
229352cf8dfcSSepherosa Ziehau 
229452cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22958ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
229652cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2297d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22988892ea20SAggelos Economopoulos 		return;
22998892ea20SAggelos Economopoulos 	}
23008892ea20SAggelos Economopoulos 
230152cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
23028892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
23038892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
23048892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
23058892ea20SAggelos Economopoulos 
230652cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
23078892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
23088892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
23098892ea20SAggelos Economopoulos 
231052cf8dfcSSepherosa Ziehau 	/*
231152cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
231252cf8dfcSSepherosa Ziehau 	 * aligned
231352cf8dfcSSepherosa Ziehau 	 */
23148892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
23158892ea20SAggelos Economopoulos 
23168892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
23178892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
231852cf8dfcSSepherosa Ziehau 
2319cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
232052cf8dfcSSepherosa Ziehau 
232152cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
232252cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
23238892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
232452cf8dfcSSepherosa Ziehau 
232552cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
232689d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
232752cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
232889d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
23298892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
233089d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
233189d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
23328892ea20SAggelos Economopoulos 	}
233373029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
23348892ea20SAggelos Economopoulos }
23358892ea20SAggelos Economopoulos 
233652cf8dfcSSepherosa Ziehau static __inline void
23372276707eSSepherosa Ziehau mxge_clean_rx_done(struct ifnet *ifp, struct mxge_rx_data *rx_data, int cycle)
23388892ea20SAggelos Economopoulos {
23395da1e9c3SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &rx_data->rx_done;
23405da1e9c3SSepherosa Ziehau 
23412276707eSSepherosa Ziehau 	while (rx_done->entry[rx_done->idx].length != 0 && cycle != 0) {
234252cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
234352cf8dfcSSepherosa Ziehau 
23448892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23458892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
234652cf8dfcSSepherosa Ziehau 
23478892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
234852cf8dfcSSepherosa Ziehau 
234960f8c66dSSepherosa Ziehau 		if (length <= MXGE_RX_SMALL_BUFLEN) {
23505da1e9c3SSepherosa Ziehau 			mxge_rx_done_small(ifp, &rx_data->rx_small,
23515da1e9c3SSepherosa Ziehau 			    length, checksum);
23525da1e9c3SSepherosa Ziehau 		} else {
23535da1e9c3SSepherosa Ziehau 			mxge_rx_done_big(ifp, &rx_data->rx_big,
23545da1e9c3SSepherosa Ziehau 			    length, checksum);
23555da1e9c3SSepherosa Ziehau 		}
235652cf8dfcSSepherosa Ziehau 
2357a3f51d6bSSepherosa Ziehau 		rx_done->idx++;
2358a3f51d6bSSepherosa Ziehau 		rx_done->idx &= rx_done->mask;
23592276707eSSepherosa Ziehau 		--cycle;
23608892ea20SAggelos Economopoulos 	}
23618892ea20SAggelos Economopoulos }
23628892ea20SAggelos Economopoulos 
2363ddbf91b7SSepherosa Ziehau static __inline void
23645da1e9c3SSepherosa Ziehau mxge_tx_done(struct ifnet *ifp, mxge_tx_ring_t *tx, uint32_t mcp_idx)
23658892ea20SAggelos Economopoulos {
236626634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
236766e7a0e8SSepherosa Ziehau 
23688892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
236966e7a0e8SSepherosa Ziehau 		struct mbuf *m;
237066e7a0e8SSepherosa Ziehau 		int idx;
237166e7a0e8SSepherosa Ziehau 
23728892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23738892ea20SAggelos Economopoulos 		tx->done++;
237466e7a0e8SSepherosa Ziehau 
23758892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
237666e7a0e8SSepherosa Ziehau 		/*
237766e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
237866e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
237966e7a0e8SSepherosa Ziehau 		 */
23808892ea20SAggelos Economopoulos 		if (m != NULL) {
238148d12a0bSSepherosa Ziehau 			tx->pkt_done++;
2382cc9c62a4SSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
23838892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
238466e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23858892ea20SAggelos Economopoulos 			m_freem(m);
23868892ea20SAggelos Economopoulos 		}
23878892ea20SAggelos Economopoulos 	}
23888892ea20SAggelos Economopoulos 
238966e7a0e8SSepherosa Ziehau 	/*
239066e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
239166e7a0e8SSepherosa Ziehau 	 * its OK to send packets
239266e7a0e8SSepherosa Ziehau 	 */
239383c2b410SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 2) {
2394aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
2395aca8f373SSepherosa Ziehau 		if (tx->req == tx->done) {
2396aca8f373SSepherosa Ziehau 			/* Reset watchdog */
2397aca8f373SSepherosa Ziehau 			tx->watchdog.wd_timer = 0;
2398aca8f373SSepherosa Ziehau 		}
2399ca8ca004SSepherosa Ziehau 	}
240089d55360SSepherosa Ziehau 
2401aca8f373SSepherosa Ziehau 	if (!ifsq_is_empty(tx->ifsq))
2402aca8f373SSepherosa Ziehau 		ifsq_devstart(tx->ifsq);
2403adb0d705SSepherosa Ziehau 
2404adb0d705SSepherosa Ziehau 	if (tx->send_stop != NULL && tx->req == tx->done) {
2405adb0d705SSepherosa Ziehau 		/*
2406adb0d705SSepherosa Ziehau 		 * Let the NIC stop polling this queue, since there
2407adb0d705SSepherosa Ziehau 		 * are no more transmits pending
2408adb0d705SSepherosa Ziehau 		 */
2409adb0d705SSepherosa Ziehau 		*tx->send_stop = 1;
2410adb0d705SSepherosa Ziehau 		tx->queue_active = 0;
2411adb0d705SSepherosa Ziehau 		tx->deactivate++;
2412adb0d705SSepherosa Ziehau 		wmb();
2413adb0d705SSepherosa Ziehau 	}
24148892ea20SAggelos Economopoulos }
24158892ea20SAggelos Economopoulos 
241689d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24178892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24188892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24198892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2420166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 5),	"10GBASE-ER"},
24218892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2422166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 3),	"10GBASE-SW"},
2423166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 2),	"10GBASE-LW"},
2424166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 1),	"10GBASE-EW"},
2425166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 0),	"Reserved"}
24268892ea20SAggelos Economopoulos };
242789d55360SSepherosa Ziehau 
242889d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
242989d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
2430166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 7),	"Reserved"},
24318892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24328892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
243389d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
243489d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24358892ea20SAggelos Economopoulos };
24368892ea20SAggelos Economopoulos 
24378892ea20SAggelos Economopoulos static void
243889d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24398892ea20SAggelos Economopoulos {
244000f2de12SSepherosa Ziehau 	int fc_opt = 0;
244100f2de12SSepherosa Ziehau 
2442166c46afSSepherosa Ziehau 	if (media_type == IFM_NONE)
2443166c46afSSepherosa Ziehau 		return;
2444166c46afSSepherosa Ziehau 
244500f2de12SSepherosa Ziehau 	if (sc->pause)
244600f2de12SSepherosa Ziehau 		fc_opt = IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
244700f2de12SSepherosa Ziehau 
244800f2de12SSepherosa Ziehau 	ifmedia_add(&sc->media, MXGE_IFM | media_type, 0, NULL);
244900f2de12SSepherosa Ziehau 	ifmedia_set(&sc->media, MXGE_IFM | media_type | fc_opt);
245000f2de12SSepherosa Ziehau 
245189d55360SSepherosa Ziehau 	sc->current_media = media_type;
2452166c46afSSepherosa Ziehau }
2453166c46afSSepherosa Ziehau 
2454166c46afSSepherosa Ziehau static void
2455166c46afSSepherosa Ziehau mxge_media_unset(mxge_softc_t *sc)
2456166c46afSSepherosa Ziehau {
2457166c46afSSepherosa Ziehau 	ifmedia_removeall(&sc->media);
2458166c46afSSepherosa Ziehau 	sc->current_media = IFM_NONE;
24598892ea20SAggelos Economopoulos }
24608892ea20SAggelos Economopoulos 
24618892ea20SAggelos Economopoulos static void
246289d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24638892ea20SAggelos Economopoulos {
2464c7431c78SSepherosa Ziehau 	const char *ptr;
246589d55360SSepherosa Ziehau 	int i;
24668892ea20SAggelos Economopoulos 
2467166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
24688892ea20SAggelos Economopoulos 
24698892ea20SAggelos Economopoulos 	/*
24702f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24718892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24728892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24738892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24748892ea20SAggelos Economopoulos 	 */
24758892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24768892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2477af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
247889d55360SSepherosa Ziehau 		return;
24798892ea20SAggelos Economopoulos 	}
24808892ea20SAggelos Economopoulos 
24818892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
248289d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24838892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2484af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24858892ea20SAggelos Economopoulos 			return;
24868892ea20SAggelos Economopoulos 		}
24878892ea20SAggelos Economopoulos 	}
248889d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24898892ea20SAggelos Economopoulos 		/* -C is CX4 */
249089d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
249189d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
249289d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24938892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
249489d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2495af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24962f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
249789d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
249889d55360SSepherosa Ziehau 		/* -R is XFP */
249989d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
2500166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
250189d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
250289d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
250389d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
2504166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
250589d55360SSepherosa Ziehau 	} else {
2506166c46afSSepherosa Ziehau 		sc->connector = MXGE_UNK;
2507af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
250889d55360SSepherosa Ziehau 	}
25098892ea20SAggelos Economopoulos }
25108892ea20SAggelos Economopoulos 
251189d55360SSepherosa Ziehau /*
251289d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
251389d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
251489d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
251589d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
251689d55360SSepherosa Ziehau  * than in the interrupt handler itself.
251789d55360SSepherosa Ziehau  */
251889d55360SSepherosa Ziehau static void
251989d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
252089d55360SSepherosa Ziehau {
252189d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
25227cc92483SSepherosa Ziehau 	const char *cage_type;
252389d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
252489d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
252589d55360SSepherosa Ziehau 	uint32_t byte;
252689d55360SSepherosa Ziehau 
252789d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
252889d55360SSepherosa Ziehau 
252989d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
25308892ea20SAggelos Economopoulos 		/* -R is XFP */
25318892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
2532b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_xfp_media_types);
25338892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25348892ea20SAggelos Economopoulos 		cage_type = "XFP";
253589d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25368892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25378892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
2538b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_sfp_media_types);
25398892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25408892ea20SAggelos Economopoulos 		byte = 3;
254189d55360SSepherosa Ziehau 	} else {
254289d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25438892ea20SAggelos Economopoulos 		return;
25448892ea20SAggelos Economopoulos 	}
25458892ea20SAggelos Economopoulos 
25468892ea20SAggelos Economopoulos 	/*
25478892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25488892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25498892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25508892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25518892ea20SAggelos Economopoulos 	 * a millisecond
25528892ea20SAggelos Economopoulos 	 */
25538892ea20SAggelos Economopoulos 
255457e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
25558892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25568892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25578892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
2558166c46afSSepherosa Ziehau 	if (err != MXGEFW_CMD_OK) {
25597cc92483SSepherosa Ziehau 		if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2560af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "failed to read XFP\n");
2561166c46afSSepherosa Ziehau 		else if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2562af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
2563166c46afSSepherosa Ziehau 		else
2564166c46afSSepherosa Ziehau 			if_printf(sc->ifp, "I2C read failed, err: %d", err);
2565166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25668892ea20SAggelos Economopoulos 		return;
2567166c46afSSepherosa Ziehau 	}
25688892ea20SAggelos Economopoulos 
25697cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25708892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25718892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25727cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25738892ea20SAggelos Economopoulos 		DELAY(1000);
25748892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25758892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25768892ea20SAggelos Economopoulos 	}
25778892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2578af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25798892ea20SAggelos Economopoulos 		    cage_type, err, ms);
2580166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25818892ea20SAggelos Economopoulos 		return;
25828892ea20SAggelos Economopoulos 	}
25838892ea20SAggelos Economopoulos 
25848892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25857cc92483SSepherosa Ziehau 		if (bootverbose) {
2586af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25878892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25887cc92483SSepherosa Ziehau 		}
258989d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
2590166c46afSSepherosa Ziehau 			mxge_media_unset(sc);
259189d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
259289d55360SSepherosa Ziehau 		}
25938892ea20SAggelos Economopoulos 		return;
25948892ea20SAggelos Economopoulos 	}
25958892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25968892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25977cc92483SSepherosa Ziehau 			if (bootverbose) {
2598af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
25998892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
26007cc92483SSepherosa Ziehau 			}
26018892ea20SAggelos Economopoulos 
260289d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
2603166c46afSSepherosa Ziehau 				mxge_media_unset(sc);
260489d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
260589d55360SSepherosa Ziehau 			}
26068892ea20SAggelos Economopoulos 			return;
26078892ea20SAggelos Economopoulos 		}
26088892ea20SAggelos Economopoulos 	}
2609166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
26107cc92483SSepherosa Ziehau 	if (bootverbose) {
2611af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
26127cc92483SSepherosa Ziehau 		    cmd.data0);
26137cc92483SSepherosa Ziehau 	}
26148892ea20SAggelos Economopoulos }
26158892ea20SAggelos Economopoulos 
26168892ea20SAggelos Economopoulos static void
2617cf5afd69SSepherosa Ziehau mxge_intr_status(struct mxge_softc *sc, const mcp_irq_data_t *stats)
26188892ea20SAggelos Economopoulos {
26198892ea20SAggelos Economopoulos 	if (sc->link_state != stats->link_up) {
26208892ea20SAggelos Economopoulos 		sc->link_state = stats->link_up;
26218892ea20SAggelos Economopoulos 		if (sc->link_state) {
262273a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_UP;
262373a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26247cc92483SSepherosa Ziehau 			if (bootverbose)
2625cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link up\n");
26268892ea20SAggelos Economopoulos 		} else {
262773a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
262873a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26297cc92483SSepherosa Ziehau 			if (bootverbose)
2630cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link down\n");
26318892ea20SAggelos Economopoulos 		}
26328892ea20SAggelos Economopoulos 		sc->need_media_probe = 1;
26338892ea20SAggelos Economopoulos 	}
2634cf5afd69SSepherosa Ziehau 
2635cf5afd69SSepherosa Ziehau 	if (sc->rdma_tags_available != be32toh(stats->rdma_tags_available)) {
2636cf5afd69SSepherosa Ziehau 		sc->rdma_tags_available = be32toh(stats->rdma_tags_available);
2637cf5afd69SSepherosa Ziehau 		if_printf(sc->ifp, "RDMA timed out! %d tags left\n",
2638cf5afd69SSepherosa Ziehau 		    sc->rdma_tags_available);
26398892ea20SAggelos Economopoulos 	}
26408892ea20SAggelos Economopoulos 
26418892ea20SAggelos Economopoulos 	if (stats->link_down) {
26428892ea20SAggelos Economopoulos 		sc->down_cnt += stats->link_down;
26438892ea20SAggelos Economopoulos 		sc->link_state = 0;
2644f0115d64SAggelos Economopoulos 		sc->ifp->if_link_state = LINK_STATE_DOWN;
2645f0115d64SAggelos Economopoulos 		if_link_state_change(sc->ifp);
26468892ea20SAggelos Economopoulos 	}
26478892ea20SAggelos Economopoulos }
26488892ea20SAggelos Economopoulos 
2649cf5afd69SSepherosa Ziehau static void
265026634ef8SSepherosa Ziehau mxge_serialize_skipmain(struct mxge_softc *sc)
265126634ef8SSepherosa Ziehau {
265226634ef8SSepherosa Ziehau 	lwkt_serialize_array_enter(sc->serializes, sc->nserialize, 1);
265326634ef8SSepherosa Ziehau }
265426634ef8SSepherosa Ziehau 
265526634ef8SSepherosa Ziehau static void
265626634ef8SSepherosa Ziehau mxge_deserialize_skipmain(struct mxge_softc *sc)
265726634ef8SSepherosa Ziehau {
265826634ef8SSepherosa Ziehau 	lwkt_serialize_array_exit(sc->serializes, sc->nserialize, 1);
265926634ef8SSepherosa Ziehau }
266026634ef8SSepherosa Ziehau 
266126634ef8SSepherosa Ziehau static void
2662cf5afd69SSepherosa Ziehau mxge_legacy(void *arg)
2663cf5afd69SSepherosa Ziehau {
2664cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2665cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2666cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2667cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26689a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2669cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2670cf5afd69SSepherosa Ziehau 	uint8_t valid;
2671cf5afd69SSepherosa Ziehau 
267226634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
267326634ef8SSepherosa Ziehau 
2674cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2675cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2676cf5afd69SSepherosa Ziehau 		return;
2677cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2678cf5afd69SSepherosa Ziehau 
2679cf5afd69SSepherosa Ziehau 	/* Lower legacy IRQ */
2680cf5afd69SSepherosa Ziehau 	*sc->irq_deassert = 0;
2681cf5afd69SSepherosa Ziehau 	if (!mxge_deassert_wait) {
2682cf5afd69SSepherosa Ziehau 		/* Don't wait for conf. that irq is low */
2683cf5afd69SSepherosa Ziehau 		stats->valid = 0;
2684cf5afd69SSepherosa Ziehau 	}
2685cf5afd69SSepherosa Ziehau 
268626634ef8SSepherosa Ziehau 	mxge_serialize_skipmain(sc);
268726634ef8SSepherosa Ziehau 
2688cf5afd69SSepherosa Ziehau 	/*
2689cf5afd69SSepherosa Ziehau 	 * Loop while waiting for legacy irq deassertion
2690cf5afd69SSepherosa Ziehau 	 * XXX do we really want to loop?
2691cf5afd69SSepherosa Ziehau 	 */
2692cf5afd69SSepherosa Ziehau 	do {
2693cf5afd69SSepherosa Ziehau 		/* Check for transmit completes and receives */
2694cf5afd69SSepherosa Ziehau 		send_done_count = be32toh(stats->send_done_count);
2695cf5afd69SSepherosa Ziehau 		while ((send_done_count != tx->pkt_done) ||
2696cf5afd69SSepherosa Ziehau 		       (rx_done->entry[rx_done->idx].length != 0)) {
26975da1e9c3SSepherosa Ziehau 			if (send_done_count != tx->pkt_done) {
26985da1e9c3SSepherosa Ziehau 				mxge_tx_done(&sc->arpcom.ac_if, tx,
26995da1e9c3SSepherosa Ziehau 				    (int)send_done_count);
27005da1e9c3SSepherosa Ziehau 			}
27012276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2702cf5afd69SSepherosa Ziehau 			send_done_count = be32toh(stats->send_done_count);
2703cf5afd69SSepherosa Ziehau 		}
2704cf5afd69SSepherosa Ziehau 		if (mxge_deassert_wait)
2705cf5afd69SSepherosa Ziehau 			wmb();
2706cf5afd69SSepherosa Ziehau 	} while (*((volatile uint8_t *)&stats->valid));
2707cf5afd69SSepherosa Ziehau 
270826634ef8SSepherosa Ziehau 	mxge_deserialize_skipmain(sc);
270926634ef8SSepherosa Ziehau 
2710cf5afd69SSepherosa Ziehau 	/* Fw link & error stats meaningful only on the first slice */
2711cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2712cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2713cf5afd69SSepherosa Ziehau 
2714cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
2715cf5afd69SSepherosa Ziehau 	if (valid & 0x1)
2716cf5afd69SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2717cf5afd69SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2718cf5afd69SSepherosa Ziehau }
2719cf5afd69SSepherosa Ziehau 
2720cf5afd69SSepherosa Ziehau static void
2721cf5afd69SSepherosa Ziehau mxge_msi(void *arg)
2722cf5afd69SSepherosa Ziehau {
2723cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2724cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2725cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2726cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
27279a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2728cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2729cf5afd69SSepherosa Ziehau 	uint8_t valid;
27302276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
27312276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
27322276707eSSepherosa Ziehau #else
27332276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
27342276707eSSepherosa Ziehau #endif
2735cf5afd69SSepherosa Ziehau 
273626634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
273726634ef8SSepherosa Ziehau 
2738cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
273976b52164SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2740cf5afd69SSepherosa Ziehau 		return;
2741cf5afd69SSepherosa Ziehau 
2742cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2743cf5afd69SSepherosa Ziehau 	stats->valid = 0;
2744cf5afd69SSepherosa Ziehau 
27452276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27462276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27472276707eSSepherosa Ziehau 		polling = TRUE;
27482276707eSSepherosa Ziehau #endif
27492276707eSSepherosa Ziehau 
27502276707eSSepherosa Ziehau 	if (!polling) {
2751cf5afd69SSepherosa Ziehau 		/* Check for receives */
275226634ef8SSepherosa Ziehau 		lwkt_serialize_enter(&ss->rx_data.rx_serialize);
2753cf5afd69SSepherosa Ziehau 		if (rx_done->entry[rx_done->idx].length != 0)
27542276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
275526634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&ss->rx_data.rx_serialize);
27562276707eSSepherosa Ziehau 	}
2757cf5afd69SSepherosa Ziehau 
275845bc9b9dSSepherosa Ziehau 	/*
275945bc9b9dSSepherosa Ziehau 	 * Check for transmit completes
276045bc9b9dSSepherosa Ziehau 	 *
276145bc9b9dSSepherosa Ziehau 	 * NOTE:
276245bc9b9dSSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
276345bc9b9dSSepherosa Ziehau 	 * which is called only in interrupt handler, the
276445bc9b9dSSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
276545bc9b9dSSepherosa Ziehau 	 */
2766cf5afd69SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
276745bc9b9dSSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
276845bc9b9dSSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
27695da1e9c3SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
277026634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
277145bc9b9dSSepherosa Ziehau 	}
2772cf5afd69SSepherosa Ziehau 
2773cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2774cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2775cf5afd69SSepherosa Ziehau 
2776cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
27772276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
27788892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
27798892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
27808892ea20SAggelos Economopoulos }
27818892ea20SAggelos Economopoulos 
27828892ea20SAggelos Economopoulos static void
2783e6c7b753SSepherosa Ziehau mxge_msix_rx(void *arg)
2784e6c7b753SSepherosa Ziehau {
2785e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2786e6c7b753SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2787e6c7b753SSepherosa Ziehau 
27882276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27892276707eSSepherosa Ziehau 	if (ss->sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27902276707eSSepherosa Ziehau 		return;
27912276707eSSepherosa Ziehau #endif
27922276707eSSepherosa Ziehau 
2793e6c7b753SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2794e6c7b753SSepherosa Ziehau 
2795e6c7b753SSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0)
27962276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, -1);
2797e6c7b753SSepherosa Ziehau 
2798e6c7b753SSepherosa Ziehau 	*ss->irq_claim = be32toh(3);
2799e6c7b753SSepherosa Ziehau }
2800e6c7b753SSepherosa Ziehau 
2801e6c7b753SSepherosa Ziehau static void
2802aca8f373SSepherosa Ziehau mxge_msix_rxtx(void *arg)
2803aca8f373SSepherosa Ziehau {
2804aca8f373SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2805aca8f373SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2806aca8f373SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2807aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
2808aca8f373SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2809aca8f373SSepherosa Ziehau 	uint32_t send_done_count;
2810aca8f373SSepherosa Ziehau 	uint8_t valid;
28112276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
28122276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
28132276707eSSepherosa Ziehau #else
28142276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
28152276707eSSepherosa Ziehau #endif
2816aca8f373SSepherosa Ziehau 
2817aca8f373SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2818aca8f373SSepherosa Ziehau 
2819aca8f373SSepherosa Ziehau 	/* Make sure the DMA has finished */
2820aca8f373SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2821aca8f373SSepherosa Ziehau 		return;
2822aca8f373SSepherosa Ziehau 
2823aca8f373SSepherosa Ziehau 	valid = stats->valid;
2824aca8f373SSepherosa Ziehau 	stats->valid = 0;
2825aca8f373SSepherosa Ziehau 
28262276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
28272276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
28282276707eSSepherosa Ziehau 		polling = TRUE;
28292276707eSSepherosa Ziehau #endif
28302276707eSSepherosa Ziehau 
2831aca8f373SSepherosa Ziehau 	/* Check for receives */
28322276707eSSepherosa Ziehau 	if (!polling && rx_done->entry[rx_done->idx].length != 0)
28332276707eSSepherosa Ziehau 		mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2834aca8f373SSepherosa Ziehau 
2835aca8f373SSepherosa Ziehau 	/*
2836aca8f373SSepherosa Ziehau 	 * Check for transmit completes
2837aca8f373SSepherosa Ziehau 	 *
2838aca8f373SSepherosa Ziehau 	 * NOTE:
2839aca8f373SSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
2840aca8f373SSepherosa Ziehau 	 * which is called only in interrupt handler, the
2841aca8f373SSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
2842aca8f373SSepherosa Ziehau 	 */
2843aca8f373SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
2844aca8f373SSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
2845aca8f373SSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
2846aca8f373SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
2847aca8f373SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
2848aca8f373SSepherosa Ziehau 	}
2849aca8f373SSepherosa Ziehau 
2850aca8f373SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
28512276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
2852aca8f373SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2853aca8f373SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2854aca8f373SSepherosa Ziehau }
2855aca8f373SSepherosa Ziehau 
2856aca8f373SSepherosa Ziehau static void
28578892ea20SAggelos Economopoulos mxge_init(void *arg)
28588892ea20SAggelos Economopoulos {
285989d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
286089d55360SSepherosa Ziehau 
286126634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(sc->ifp);
286289d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
286389d55360SSepherosa Ziehau 		mxge_open(sc);
28648892ea20SAggelos Economopoulos }
28658892ea20SAggelos Economopoulos 
28668892ea20SAggelos Economopoulos static void
28678892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
28688892ea20SAggelos Economopoulos {
28698892ea20SAggelos Economopoulos 	int i;
28708892ea20SAggelos Economopoulos 
28719a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
28729a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.info[i].m == NULL)
28738892ea20SAggelos Economopoulos 			continue;
28749a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_big.dmat,
28759a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map);
28769a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_big.info[i].m);
28779a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info[i].m = NULL;
28788892ea20SAggelos Economopoulos 	}
28798892ea20SAggelos Economopoulos 
28809a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
28819a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.info[i].m == NULL)
28828892ea20SAggelos Economopoulos 			continue;
28839a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_small.dmat,
28849a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map);
28859a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_small.info[i].m);
28869a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info[i].m = NULL;
28878892ea20SAggelos Economopoulos 	}
28888892ea20SAggelos Economopoulos 
28894e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
28908892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
28918892ea20SAggelos Economopoulos 		return;
28928892ea20SAggelos Economopoulos 
28938892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
28948892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
28958892ea20SAggelos Economopoulos 			continue;
28964e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
28978892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
28988892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
28998892ea20SAggelos Economopoulos 	}
29008892ea20SAggelos Economopoulos }
29018892ea20SAggelos Economopoulos 
29028892ea20SAggelos Economopoulos static void
29038892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
29048892ea20SAggelos Economopoulos {
29058892ea20SAggelos Economopoulos 	int slice;
29068892ea20SAggelos Economopoulos 
29078892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29088892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
29098892ea20SAggelos Economopoulos }
29108892ea20SAggelos Economopoulos 
29118892ea20SAggelos Economopoulos static void
29128892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
29138892ea20SAggelos Economopoulos {
29148892ea20SAggelos Economopoulos 	int i;
29158892ea20SAggelos Economopoulos 
29169a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_done.entry != NULL) {
2917414caf0dSSepherosa Ziehau 		mxge_dma_free(&ss->rx_done_dma);
29189a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = NULL;
2919798c3369SSepherosa Ziehau 	}
29208892ea20SAggelos Economopoulos 
292111868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
292211868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
292311868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2924798c3369SSepherosa Ziehau 	}
29258892ea20SAggelos Economopoulos 
2926798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2927d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
29288892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2929798c3369SSepherosa Ziehau 	}
29308892ea20SAggelos Economopoulos 
29319a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.shadow != NULL) {
29329a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.shadow, M_DEVBUF);
29339a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.shadow = NULL;
2934798c3369SSepherosa Ziehau 	}
29358892ea20SAggelos Economopoulos 
29369a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.shadow != NULL) {
29379a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.shadow, M_DEVBUF);
29389a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow = NULL;
2939798c3369SSepherosa Ziehau 	}
29408892ea20SAggelos Economopoulos 
29418892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
29428892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
29438892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
29448892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
29458892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
29468892ea20SAggelos Economopoulos 			}
29478892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
29488892ea20SAggelos Economopoulos 		}
2949d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
29508892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2951798c3369SSepherosa Ziehau 	}
29528892ea20SAggelos Economopoulos 
29539a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.info != NULL) {
29549a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.dmat != NULL) {
29559a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
29569a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29579a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[i].map);
29588892ea20SAggelos Economopoulos 			}
29599a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29609a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
29619a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29628892ea20SAggelos Economopoulos 		}
29639a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.info, M_DEVBUF);
29649a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info = NULL;
2965798c3369SSepherosa Ziehau 	}
29668892ea20SAggelos Economopoulos 
29679a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.info != NULL) {
29689a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.dmat != NULL) {
29699a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
29709a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29719a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[i].map);
29728892ea20SAggelos Economopoulos 			}
29739a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29749a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
29759a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29768892ea20SAggelos Economopoulos 		}
29779a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.info, M_DEVBUF);
29789a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info = NULL;
29798892ea20SAggelos Economopoulos 	}
2980798c3369SSepherosa Ziehau }
29818892ea20SAggelos Economopoulos 
29828892ea20SAggelos Economopoulos static void
29838892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
29848892ea20SAggelos Economopoulos {
29858892ea20SAggelos Economopoulos 	int slice;
29868892ea20SAggelos Economopoulos 
2987798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2988798c3369SSepherosa Ziehau 		return;
2989798c3369SSepherosa Ziehau 
29908892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29918892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
29928892ea20SAggelos Economopoulos }
29938892ea20SAggelos Economopoulos 
29948892ea20SAggelos Economopoulos static int
29958892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
29968892ea20SAggelos Economopoulos     int tx_ring_entries)
29978892ea20SAggelos Economopoulos {
29988892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
29998892ea20SAggelos Economopoulos 	size_t bytes;
30008892ea20SAggelos Economopoulos 	int err, i;
30018892ea20SAggelos Economopoulos 
30027cc92483SSepherosa Ziehau 	/*
30037cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
30047cc92483SSepherosa Ziehau 	 */
30058892ea20SAggelos Economopoulos 
30069a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.mask = ss->rx_data.rx_big.mask =
30079a4ae890SSepherosa Ziehau 	    rx_ring_entries - 1;
30089a4ae890SSepherosa Ziehau 	ss->rx_data.rx_done.mask = (2 * rx_ring_entries) - 1;
30098892ea20SAggelos Economopoulos 
30107cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
30119a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.shadow);
30129a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30138892ea20SAggelos Economopoulos 
30149a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.shadow);
30159a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30168892ea20SAggelos Economopoulos 
30177cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
30189a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.info);
30199a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30208892ea20SAggelos Economopoulos 
30219a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.info);
30229a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30238892ea20SAggelos Economopoulos 
30247cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
30258892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30268892ea20SAggelos Economopoulos 				 1,			/* alignment */
30278892ea20SAggelos Economopoulos 				 4096,			/* boundary */
30288892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30298892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30308892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
30318892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsize */
30328892ea20SAggelos Economopoulos 				 1,			/* num segs */
30338892ea20SAggelos Economopoulos 				 MHLEN,			/* maxsegsize */
30347cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
30357cc92483SSepherosa Ziehau 				 			/* flags */
30369a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_small.dmat); /* tag */
30378892ea20SAggelos Economopoulos 	if (err != 0) {
30388892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_small dmat\n",
30398892ea20SAggelos Economopoulos 		    err);
30403598cc14SSascha Wildner 		return err;
30418892ea20SAggelos Economopoulos 	}
30428892ea20SAggelos Economopoulos 
30439a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_small.dmat, BUS_DMA_WAITOK,
30449a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_small.extra_map);
3045798c3369SSepherosa Ziehau 	if (err != 0) {
3046798c3369SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_small dmamap\n", err);
30479a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
30489a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.dmat = NULL;
3049798c3369SSepherosa Ziehau 		return err;
3050798c3369SSepherosa Ziehau 	}
30519a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
30529a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_small.dmat,
30539a4ae890SSepherosa Ziehau 		    BUS_DMA_WAITOK, &ss->rx_data.rx_small.info[i].map);
3054798c3369SSepherosa Ziehau 		if (err != 0) {
3055798c3369SSepherosa Ziehau 			int j;
3056798c3369SSepherosa Ziehau 
3057798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_small dmamap\n", err);
3058798c3369SSepherosa Ziehau 
3059798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
30609a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
30619a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[j].map);
3062798c3369SSepherosa Ziehau 			}
30639a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
30649a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
30659a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
30669a4ae890SSepherosa Ziehau 			ss->rx_data.rx_small.dmat = NULL;
3067798c3369SSepherosa Ziehau 			return err;
3068798c3369SSepherosa Ziehau 		}
3069798c3369SSepherosa Ziehau 	}
3070798c3369SSepherosa Ziehau 
30718892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30728892ea20SAggelos Economopoulos 				 1,			/* alignment */
30738892ea20SAggelos Economopoulos 				 4096,			/* boundary */
30748892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30758892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
30768892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
3077b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
3078b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
30798892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
30807cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
30817cc92483SSepherosa Ziehau 				 			/* flags */
30829a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_big.dmat); /* tag */
30838892ea20SAggelos Economopoulos 	if (err != 0) {
30848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
30858892ea20SAggelos Economopoulos 		    err);
30863598cc14SSascha Wildner 		return err;
30878892ea20SAggelos Economopoulos 	}
30887cc92483SSepherosa Ziehau 
30899a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30909a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_big.extra_map);
30918892ea20SAggelos Economopoulos 	if (err != 0) {
30927cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
30939a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
30949a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.dmat = NULL;
30953598cc14SSascha Wildner 		return err;
30968892ea20SAggelos Economopoulos 	}
30979a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
30989a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30999a4ae890SSepherosa Ziehau 		    &ss->rx_data.rx_big.info[i].map);
3100798c3369SSepherosa Ziehau 		if (err != 0) {
3101798c3369SSepherosa Ziehau 			int j;
3102798c3369SSepherosa Ziehau 
3103798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
3104798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
31059a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31069a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[j].map);
3107798c3369SSepherosa Ziehau 			}
31089a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31099a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
31109a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
31119a4ae890SSepherosa Ziehau 			ss->rx_data.rx_big.dmat = NULL;
3112798c3369SSepherosa Ziehau 			return err;
3113798c3369SSepherosa Ziehau 		}
3114798c3369SSepherosa Ziehau 	}
31158892ea20SAggelos Economopoulos 
31167cc92483SSepherosa Ziehau 	/*
31177cc92483SSepherosa Ziehau 	 * Now allocate TX resources
31187cc92483SSepherosa Ziehau 	 */
31198892ea20SAggelos Economopoulos 
31208892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
31218892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
31228892ea20SAggelos Economopoulos 
3123dfda108aSSepherosa Ziehau 	/*
3124dfda108aSSepherosa Ziehau 	 * Allocate the tx request copy block; MUST be at least 8 bytes
3125dfda108aSSepherosa Ziehau 	 * aligned
3126dfda108aSSepherosa Ziehau 	 */
312711868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
3128dfda108aSSepherosa Ziehau 	ss->tx.req_list = kmalloc_cachealign(__VM_CACHELINE_ALIGN(bytes),
3129dfda108aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
31308892ea20SAggelos Economopoulos 
31317cc92483SSepherosa Ziehau 	/* Allocate the tx busdma segment list */
31328892ea20SAggelos Economopoulos 	bytes = sizeof(*ss->tx.seg_list) * ss->tx.max_desc;
31337cc92483SSepherosa Ziehau 	ss->tx.seg_list = kmalloc(bytes, M_DEVBUF, M_WAITOK);
31348892ea20SAggelos Economopoulos 
31357cc92483SSepherosa Ziehau 	/* Allocate the tx host info ring */
31368892ea20SAggelos Economopoulos 	bytes = tx_ring_entries * sizeof(*ss->tx.info);
3137d777b84fSAggelos Economopoulos 	ss->tx.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
31388892ea20SAggelos Economopoulos 
31397cc92483SSepherosa Ziehau 	/* Allocate the tx busdma resources */
31408892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
31418892ea20SAggelos Economopoulos 				 1,			/* alignment */
31428892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* boundary */
31438892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
31448892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
31458892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
31467cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
31477cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
31487cc92483SSepherosa Ziehau 				 			/* maxsize */
31498892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
31508892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
31517cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
31527cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
31538892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
31548892ea20SAggelos Economopoulos 	if (err != 0) {
31557cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
31563598cc14SSascha Wildner 		return err;
31578892ea20SAggelos Economopoulos 	}
31588892ea20SAggelos Economopoulos 
31597cc92483SSepherosa Ziehau 	/*
31607cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
31617cc92483SSepherosa Ziehau 	 */
31628892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
31637cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
31647cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
31658892ea20SAggelos Economopoulos 		if (err != 0) {
3166798c3369SSepherosa Ziehau 			int j;
3167798c3369SSepherosa Ziehau 
31687cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
3169798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
3170798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
3171798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
3172798c3369SSepherosa Ziehau 			}
3173798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
3174798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
31753598cc14SSascha Wildner 			return err;
31768892ea20SAggelos Economopoulos 		}
31778892ea20SAggelos Economopoulos 	}
31788892ea20SAggelos Economopoulos 	return 0;
31798892ea20SAggelos Economopoulos }
31808892ea20SAggelos Economopoulos 
31818892ea20SAggelos Economopoulos static int
31828892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
31838892ea20SAggelos Economopoulos {
31848892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
31858892ea20SAggelos Economopoulos 	int tx_ring_size;
31868892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
31878892ea20SAggelos Economopoulos 	int err, slice;
31888892ea20SAggelos Economopoulos 
31897cc92483SSepherosa Ziehau 	/* Get ring sizes */
31908892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
31918892ea20SAggelos Economopoulos 	if (err != 0) {
31928892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3193798c3369SSepherosa Ziehau 		return err;
31948892ea20SAggelos Economopoulos 	}
31957cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
31968892ea20SAggelos Economopoulos 
31978892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
3198089301c2SSepherosa Ziehau 	rx_ring_entries = sc->rx_intr_slots / 2;
3199aca8f373SSepherosa Ziehau 
320018f86fd8SSepherosa Ziehau 	if (bootverbose) {
320118f86fd8SSepherosa Ziehau 		device_printf(sc->dev, "tx desc %d, rx desc %d\n",
320218f86fd8SSepherosa Ziehau 		    tx_ring_entries, rx_ring_entries);
320318f86fd8SSepherosa Ziehau 	}
320418f86fd8SSepherosa Ziehau 
320514929979SSepherosa Ziehau 	sc->ifp->if_nmbclusters = rx_ring_entries * sc->num_slices;
320614929979SSepherosa Ziehau 	sc->ifp->if_nmbjclusters = sc->ifp->if_nmbclusters;
320714929979SSepherosa Ziehau 
3208f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3209f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
3210aca8f373SSepherosa Ziehau 	ifq_set_subq_cnt(&sc->ifp->if_snd, sc->num_tx_rings);
3211aca8f373SSepherosa Ziehau 
3212aca8f373SSepherosa Ziehau 	if (sc->num_tx_rings > 1) {
3213*bdbc20adSSepherosa Ziehau 		sc->ifp->if_mapsubq = ifq_mapsubq_modulo;
3214*bdbc20adSSepherosa Ziehau 		ifq_set_subq_divisor(&sc->ifp->if_snd, sc->num_tx_rings);
3215aca8f373SSepherosa Ziehau 	}
32168892ea20SAggelos Economopoulos 
32178892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
32188892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
32197cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3220798c3369SSepherosa Ziehau 		if (err != 0) {
3221798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3222798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3223798c3369SSepherosa Ziehau 			return err;
3224798c3369SSepherosa Ziehau 		}
32258892ea20SAggelos Economopoulos 	}
32268892ea20SAggelos Economopoulos 	return 0;
32278892ea20SAggelos Economopoulos }
32288892ea20SAggelos Economopoulos 
32298892ea20SAggelos Economopoulos static void
3230b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
32318892ea20SAggelos Economopoulos {
3232b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
32338892ea20SAggelos Economopoulos 
32348892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
32358892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3236b9a8961fSSepherosa Ziehau 	} else {
3237b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
32388892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
32398892ea20SAggelos Economopoulos 	}
32408892ea20SAggelos Economopoulos }
32418892ea20SAggelos Economopoulos 
32428892ea20SAggelos Economopoulos static int
3243b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
32448892ea20SAggelos Economopoulos {
32458892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32468892ea20SAggelos Economopoulos 	int err, i, slice;
32478892ea20SAggelos Economopoulos 
3248308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
32498892ea20SAggelos Economopoulos 
3250308781adSSepherosa Ziehau 	/*
3251308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3252308781adSSepherosa Ziehau 	 */
32538892ea20SAggelos Economopoulos 	err = 0;
3254aca8f373SSepherosa Ziehau 
325557e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
3256aca8f373SSepherosa Ziehau 	if (ss->sc->num_tx_rings == 1) {
3257aca8f373SSepherosa Ziehau 		if (slice == 0) {
3258aca8f373SSepherosa Ziehau 			cmd.data0 = slice;
3259aca8f373SSepherosa Ziehau 			err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET,
3260aca8f373SSepherosa Ziehau 			    &cmd);
3261aca8f373SSepherosa Ziehau 			ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3262aca8f373SSepherosa Ziehau 			    (ss->sc->sram + cmd.data0);
3263aca8f373SSepherosa Ziehau 			/* Leave send_go and send_stop as NULL */
3264aca8f373SSepherosa Ziehau 		}
3265aca8f373SSepherosa Ziehau 	} else {
3266aca8f373SSepherosa Ziehau 		cmd.data0 = slice;
3267aca8f373SSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3268aca8f373SSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3269aca8f373SSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
3270aca8f373SSepherosa Ziehau 		ss->tx.send_go = (volatile uint32_t *)
3271aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3272aca8f373SSepherosa Ziehau 		ss->tx.send_stop = (volatile uint32_t *)
3273aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3274aca8f373SSepherosa Ziehau 	}
3275308781adSSepherosa Ziehau 
32768892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3277308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
32789a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.lanai =
3279308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3280308781adSSepherosa Ziehau 
32818892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3282308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
32839a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.lanai =
3284308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
32858892ea20SAggelos Economopoulos 
32868892ea20SAggelos Economopoulos 	if (err != 0) {
3287308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
32888892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
32898892ea20SAggelos Economopoulos 		return EIO;
32908892ea20SAggelos Economopoulos 	}
32918892ea20SAggelos Economopoulos 
3292308781adSSepherosa Ziehau 	/*
3293308781adSSepherosa Ziehau 	 * Stock small receive ring
3294308781adSSepherosa Ziehau 	 */
32959a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
32969a4ae890SSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_data.rx_small,
32979a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map, i, TRUE);
32988892ea20SAggelos Economopoulos 		if (err) {
3299308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
33009a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.mask + 1);
33018892ea20SAggelos Economopoulos 			return ENOMEM;
33028892ea20SAggelos Economopoulos 		}
33038892ea20SAggelos Economopoulos 	}
3304308781adSSepherosa Ziehau 
3305308781adSSepherosa Ziehau 	/*
3306308781adSSepherosa Ziehau 	 * Stock big receive ring
3307308781adSSepherosa Ziehau 	 */
33089a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33099a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_low = 0xffffffff;
33109a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_high = 0xffffffff;
33118892ea20SAggelos Economopoulos 	}
3312308781adSSepherosa Ziehau 
33139a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.cl_size = cl_size;
3314308781adSSepherosa Ziehau 
33159a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33169a4ae890SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_data.rx_big,
33179a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map, i, TRUE);
33188892ea20SAggelos Economopoulos 		if (err) {
3319308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
33209a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.mask + 1);
33218892ea20SAggelos Economopoulos 			return ENOMEM;
33228892ea20SAggelos Economopoulos 		}
33238892ea20SAggelos Economopoulos 	}
33248892ea20SAggelos Economopoulos 	return 0;
33258892ea20SAggelos Economopoulos }
33268892ea20SAggelos Economopoulos 
33278892ea20SAggelos Economopoulos static int
33288892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
33298892ea20SAggelos Economopoulos {
33306ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
33318892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3332b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
33338892ea20SAggelos Economopoulos 	bus_addr_t bus;
33348892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
33358892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
33368892ea20SAggelos Economopoulos 
333726634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
33386ee6cba3SSepherosa Ziehau 
33398892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
33406ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
33418892ea20SAggelos Economopoulos 
33428892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
33438892ea20SAggelos Economopoulos 	if (err != 0) {
33446ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
33458892ea20SAggelos Economopoulos 		return EIO;
33468892ea20SAggelos Economopoulos 	}
33478892ea20SAggelos Economopoulos 
33488892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
3349*bdbc20adSSepherosa Ziehau 		if (sc->use_rss) {
3350*bdbc20adSSepherosa Ziehau 			volatile uint8_t *hwkey;
3351*bdbc20adSSepherosa Ziehau 			uint8_t swkey[MXGE_HWRSS_KEYLEN];
33528892ea20SAggelos Economopoulos 
3353*bdbc20adSSepherosa Ziehau 			/*
3354*bdbc20adSSepherosa Ziehau 			 * Setup the indirect table.
3355*bdbc20adSSepherosa Ziehau 			 */
3356*bdbc20adSSepherosa Ziehau 			if_ringmap_rdrtable(sc->ring_map, sc->rdr_table,
3357*bdbc20adSSepherosa Ziehau 			    NETISR_CPUMAX);
3358*bdbc20adSSepherosa Ziehau 
3359*bdbc20adSSepherosa Ziehau 			cmd.data0 = NETISR_CPUMAX;
3360*bdbc20adSSepherosa Ziehau 			err = mxge_send_cmd(sc,
3361*bdbc20adSSepherosa Ziehau 			    MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
3362*bdbc20adSSepherosa Ziehau 
3363*bdbc20adSSepherosa Ziehau 			err |= mxge_send_cmd(sc,
3364*bdbc20adSSepherosa Ziehau 			    MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
33658892ea20SAggelos Economopoulos 			if (err != 0) {
33666ee6cba3SSepherosa Ziehau 				if_printf(ifp, "failed to setup rss tables\n");
33678892ea20SAggelos Economopoulos 				return err;
33688892ea20SAggelos Economopoulos 			}
33698892ea20SAggelos Economopoulos 
33708892ea20SAggelos Economopoulos 			itable = sc->sram + cmd.data0;
3371*bdbc20adSSepherosa Ziehau 			for (i = 0; i < NETISR_CPUMAX; i++)
3372*bdbc20adSSepherosa Ziehau 				itable[i] = sc->rdr_table[i];
33738892ea20SAggelos Economopoulos 
3374*bdbc20adSSepherosa Ziehau 			/*
3375*bdbc20adSSepherosa Ziehau 			 * Setup Toeplitz key.
3376*bdbc20adSSepherosa Ziehau 			 */
33771cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_KEY_OFFSET,
33781cd61a7cSSepherosa Ziehau 			    &cmd);
33791cd61a7cSSepherosa Ziehau 			if (err != 0) {
33801cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to get rsskey\n");
33811cd61a7cSSepherosa Ziehau 				return err;
33821cd61a7cSSepherosa Ziehau 			}
33831cd61a7cSSepherosa Ziehau 			hwkey = sc->sram + cmd.data0;
33841cd61a7cSSepherosa Ziehau 
33851cd61a7cSSepherosa Ziehau 			toeplitz_get_key(swkey, MXGE_HWRSS_KEYLEN);
33861cd61a7cSSepherosa Ziehau 			for (i = 0; i < MXGE_HWRSS_KEYLEN; ++i)
33871cd61a7cSSepherosa Ziehau 				hwkey[i] = swkey[i];
33881cd61a7cSSepherosa Ziehau 			wmb();
33891cd61a7cSSepherosa Ziehau 
33901cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_RSS_KEY_UPDATED,
33911cd61a7cSSepherosa Ziehau 			    &cmd);
33921cd61a7cSSepherosa Ziehau 			if (err != 0) {
33931cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to update rsskey\n");
33941cd61a7cSSepherosa Ziehau 				return err;
33951cd61a7cSSepherosa Ziehau 			}
33961cd61a7cSSepherosa Ziehau 			if (bootverbose)
33971cd61a7cSSepherosa Ziehau 				if_printf(ifp, "RSS key updated\n");
3398*bdbc20adSSepherosa Ziehau 		} else {
3399*bdbc20adSSepherosa Ziehau 			/* Setup the indirection table */
3400*bdbc20adSSepherosa Ziehau 			cmd.data0 = sc->num_slices;
3401*bdbc20adSSepherosa Ziehau 			err = mxge_send_cmd(sc,
3402*bdbc20adSSepherosa Ziehau 			    MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
3403*bdbc20adSSepherosa Ziehau 
3404*bdbc20adSSepherosa Ziehau 			err |= mxge_send_cmd(sc,
3405*bdbc20adSSepherosa Ziehau 			    MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
3406*bdbc20adSSepherosa Ziehau 			if (err != 0) {
3407*bdbc20adSSepherosa Ziehau 				if_printf(ifp, "failed to setup rss tables\n");
3408*bdbc20adSSepherosa Ziehau 				return err;
3409*bdbc20adSSepherosa Ziehau 			}
3410*bdbc20adSSepherosa Ziehau 
3411*bdbc20adSSepherosa Ziehau 			/* Just enable an identity mapping */
3412*bdbc20adSSepherosa Ziehau 			itable = sc->sram + cmd.data0;
3413*bdbc20adSSepherosa Ziehau 			for (i = 0; i < sc->num_slices; i++)
3414*bdbc20adSSepherosa Ziehau 				itable[i] = (uint8_t)i;
34151cd61a7cSSepherosa Ziehau 		}
34161cd61a7cSSepherosa Ziehau 
34178892ea20SAggelos Economopoulos 		cmd.data0 = 1;
34188433e5f5SSepherosa Ziehau 		if (sc->use_rss) {
34198433e5f5SSepherosa Ziehau 			if (bootverbose)
34208433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: RSS\n");
3421e6c7b753SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_IPV4 |
3422e6c7b753SSepherosa Ziehau 			    MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
34238433e5f5SSepherosa Ziehau 		} else {
34248433e5f5SSepherosa Ziehau 			if (bootverbose)
34258433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: SRC_DST_PORT\n");
34268433e5f5SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
34278433e5f5SSepherosa Ziehau 		}
34288892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
34298892ea20SAggelos Economopoulos 		if (err != 0) {
34306ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
34318892ea20SAggelos Economopoulos 			return err;
34328892ea20SAggelos Economopoulos 		}
34338892ea20SAggelos Economopoulos 	}
34348892ea20SAggelos Economopoulos 
343589d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
343689d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
343789d55360SSepherosa Ziehau 	if (err) {
34386ee6cba3SSepherosa Ziehau 		/*
34396ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
34406ee6cba3SSepherosa Ziehau 		 */
34416ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
34426ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
34436ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
34446ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
344589d55360SSepherosa Ziehau 	}
34468892ea20SAggelos Economopoulos 
3447b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
34488892ea20SAggelos Economopoulos 
3449b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
34506ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
34516ee6cba3SSepherosa Ziehau 	/*
34526ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
34536ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
34546ee6cba3SSepherosa Ziehau 	 */
34556ee6cba3SSepherosa Ziehau 
34566ee6cba3SSepherosa Ziehau 	/*
34576ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
34586ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
34592f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
34606ee6cba3SSepherosa Ziehau 	 */
34616ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
34628892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
34636ee6cba3SSepherosa Ziehau 
346460f8c66dSSepherosa Ziehau 	cmd.data0 = MXGE_RX_SMALL_BUFLEN;
34656ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
34666ee6cba3SSepherosa Ziehau 
3467b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
34688892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
34698892ea20SAggelos Economopoulos 
34708892ea20SAggelos Economopoulos 	if (err != 0) {
34716ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
34728892ea20SAggelos Economopoulos 		goto abort;
34738892ea20SAggelos Economopoulos 	}
34748892ea20SAggelos Economopoulos 
34758892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
3476ebe934eaSSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; slice++) {
34778892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
34787cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34797cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34808892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
34818892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
34828892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
34838892ea20SAggelos Economopoulos 	}
34848892ea20SAggelos Economopoulos 
34858892ea20SAggelos Economopoulos 	if (err != 0) {
34867cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
34878892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
34888892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
34898892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
34906ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
34918892ea20SAggelos Economopoulos 		    &cmd);
34926ee6cba3SSepherosa Ziehau 
34938892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
34948892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
34958892ea20SAggelos Economopoulos 	} else {
34968892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
34978892ea20SAggelos Economopoulos 	}
34988892ea20SAggelos Economopoulos 
34998892ea20SAggelos Economopoulos 	if (err != 0) {
35006ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
35018892ea20SAggelos Economopoulos 		goto abort;
35028892ea20SAggelos Economopoulos 	}
35038892ea20SAggelos Economopoulos 
35048892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3505b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
35068892ea20SAggelos Economopoulos 		if (err != 0) {
35076ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
35088892ea20SAggelos Economopoulos 			goto abort;
35098892ea20SAggelos Economopoulos 		}
35108892ea20SAggelos Economopoulos 	}
35118892ea20SAggelos Economopoulos 
35128892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
35138892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
35148892ea20SAggelos Economopoulos 	if (err) {
35156ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
35168892ea20SAggelos Economopoulos 		goto abort;
35178892ea20SAggelos Economopoulos 	}
3518aca8f373SSepherosa Ziehau 
35196ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
3520aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3521aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3522aca8f373SSepherosa Ziehau 
3523aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3524aca8f373SSepherosa Ziehau 		ifsq_watchdog_start(&tx->watchdog);
3525aca8f373SSepherosa Ziehau 	}
35268892ea20SAggelos Economopoulos 
35278892ea20SAggelos Economopoulos 	return 0;
35288892ea20SAggelos Economopoulos 
35298892ea20SAggelos Economopoulos abort:
35308892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
35318892ea20SAggelos Economopoulos 	return err;
35328892ea20SAggelos Economopoulos }
35338892ea20SAggelos Economopoulos 
35342c29ffc6SSepherosa Ziehau static void
353589d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
35368892ea20SAggelos Economopoulos {
35372c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
35388892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3539aca8f373SSepherosa Ziehau 	int err, old_down_cnt, i;
35408892ea20SAggelos Economopoulos 
354126634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
354289d55360SSepherosa Ziehau 
354389d55360SSepherosa Ziehau 	if (!down) {
35448892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
35458892ea20SAggelos Economopoulos 		wmb();
35462c29ffc6SSepherosa Ziehau 
35478892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
35482c29ffc6SSepherosa Ziehau 		if (err)
35492c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
35502c29ffc6SSepherosa Ziehau 
35518892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
3552aca8f373SSepherosa Ziehau 			/*
3553aca8f373SSepherosa Ziehau 			 * Wait for down irq
3554aca8f373SSepherosa Ziehau 			 * XXX racy
3555aca8f373SSepherosa Ziehau 			 */
355626634ef8SSepherosa Ziehau 			ifnet_deserialize_all(ifp);
35578892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
355826634ef8SSepherosa Ziehau 			ifnet_serialize_all(ifp);
35598892ea20SAggelos Economopoulos 		}
35602c29ffc6SSepherosa Ziehau 
35618892ea20SAggelos Economopoulos 		wmb();
35622c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
35632c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
356489d55360SSepherosa Ziehau 	}
35658892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
3566aca8f373SSepherosa Ziehau 
3567aca8f373SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
3568aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3569aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3570aca8f373SSepherosa Ziehau 
3571aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3572aca8f373SSepherosa Ziehau 		ifsq_watchdog_stop(&tx->watchdog);
3573aca8f373SSepherosa Ziehau 	}
35748892ea20SAggelos Economopoulos }
35758892ea20SAggelos Economopoulos 
35768892ea20SAggelos Economopoulos static void
35778892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
35788892ea20SAggelos Economopoulos {
35798892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
35808892ea20SAggelos Economopoulos 	int reg;
358189d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
35828892ea20SAggelos Economopoulos 
35837cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
35848892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
35858892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
35868892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
35878892ea20SAggelos Economopoulos 
358889d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
35898892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
35908892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
35918892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
359289d55360SSepherosa Ziehau 			sc->pectl = pectl;
359389d55360SSepherosa Ziehau 		} else {
35947cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
359589d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
359689d55360SSepherosa Ziehau 		}
35978892ea20SAggelos Economopoulos 	}
35988892ea20SAggelos Economopoulos 
35997cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
36008892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
36018892ea20SAggelos Economopoulos }
36028892ea20SAggelos Economopoulos 
36038892ea20SAggelos Economopoulos static uint32_t
36048892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
36058892ea20SAggelos Economopoulos {
36068892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
36078892ea20SAggelos Economopoulos 	uint32_t vs;
36088892ea20SAggelos Economopoulos 
36098a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
36108892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
36118a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
36128892ea20SAggelos Economopoulos 		return (uint32_t)-1;
36138892ea20SAggelos Economopoulos 	}
36148a20b038SSepherosa Ziehau 	/* Enable read32 mode */
36158892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
36168a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
36178892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
36188a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
36198892ea20SAggelos Economopoulos }
36208892ea20SAggelos Economopoulos 
362189d55360SSepherosa Ziehau static void
362289d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
36238892ea20SAggelos Economopoulos {
36248892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
362589d55360SSepherosa Ziehau 	int err, running;
36268892ea20SAggelos Economopoulos 	uint32_t reboot;
36278892ea20SAggelos Economopoulos 	uint16_t cmd;
36288892ea20SAggelos Economopoulos 
36298892ea20SAggelos Economopoulos 	err = ENXIO;
36308892ea20SAggelos Economopoulos 
36318a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
36328892ea20SAggelos Economopoulos 
36338892ea20SAggelos Economopoulos 	/*
36348a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
36358892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
36368892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
36378892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
36388892ea20SAggelos Economopoulos 	 * again
36398892ea20SAggelos Economopoulos 	 */
36408892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36418892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
36428892ea20SAggelos Economopoulos 		/*
36438a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
36448892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
36458892ea20SAggelos Economopoulos 		 * back, then give up
36468892ea20SAggelos Economopoulos 		 */
36478892ea20SAggelos Economopoulos 		DELAY(1000*100);
36488892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36498a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
36508a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
36518892ea20SAggelos Economopoulos 	}
36528892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
36538a20b038SSepherosa Ziehau 		/* Print the reboot status */
36548892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
36558a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
36568a20b038SSepherosa Ziehau 
365789d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
365889d55360SSepherosa Ziehau 		if (running) {
365989d55360SSepherosa Ziehau 			/*
36608a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
366189d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
366289d55360SSepherosa Ziehau 			 */
366389d55360SSepherosa Ziehau 
366489d55360SSepherosa Ziehau 			/* Mark the link as down */
366589d55360SSepherosa Ziehau 			if (sc->link_state) {
366689d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
366789d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
366889d55360SSepherosa Ziehau 			}
366989d55360SSepherosa Ziehau 			mxge_close(sc, 1);
367089d55360SSepherosa Ziehau 		}
36718a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
36728892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
36738892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
36748892ea20SAggelos Economopoulos 
36758a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
36768892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
36778892ea20SAggelos Economopoulos 
36788a20b038SSepherosa Ziehau 		/* Reload f/w */
367989d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
36808a20b038SSepherosa Ziehau 		if (err)
36818a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
36828a20b038SSepherosa Ziehau 		if (running && !err) {
3683aca8f373SSepherosa Ziehau 			int i;
3684aca8f373SSepherosa Ziehau 
368589d55360SSepherosa Ziehau 			err = mxge_open(sc);
3686aca8f373SSepherosa Ziehau 
3687aca8f373SSepherosa Ziehau 			for (i = 0; i < sc->num_tx_rings; ++i)
3688aca8f373SSepherosa Ziehau 				ifsq_devstart_sched(sc->ss[i].tx.ifsq);
368989d55360SSepherosa Ziehau 		}
369089d55360SSepherosa Ziehau 		sc->watchdog_resets++;
369189d55360SSepherosa Ziehau 	} else {
36928a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
369389d55360SSepherosa Ziehau 		err = 0;
369489d55360SSepherosa Ziehau 	}
369589d55360SSepherosa Ziehau 	if (err) {
36968a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
369789d55360SSepherosa Ziehau 	} else {
369889d55360SSepherosa Ziehau 		if (sc->dying == 2)
369989d55360SSepherosa Ziehau 			sc->dying = 0;
370089d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
370189d55360SSepherosa Ziehau 	}
370289d55360SSepherosa Ziehau }
370389d55360SSepherosa Ziehau 
370489d55360SSepherosa Ziehau static void
370589d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
370689d55360SSepherosa Ziehau {
37078a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
37088a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
37098892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
37108a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
37118892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
37128a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
37138a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
37148892ea20SAggelos Economopoulos }
37158892ea20SAggelos Economopoulos 
371689d55360SSepherosa Ziehau static u_long
37178892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
37188892ea20SAggelos Economopoulos {
3719cc9c62a4SSepherosa Ziehau 	u_long ipackets, opackets, pkts;
37208892ea20SAggelos Economopoulos 
3721cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, ipackets);
3722cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, opackets);
372389d55360SSepherosa Ziehau 
3724cc9c62a4SSepherosa Ziehau 	pkts = ipackets - sc->ipackets;
3725cc9c62a4SSepherosa Ziehau 	pkts += opackets - sc->opackets;
372689d55360SSepherosa Ziehau 
3727cc9c62a4SSepherosa Ziehau 	sc->ipackets = ipackets;
3728cc9c62a4SSepherosa Ziehau 	sc->opackets = opackets;
3729cc9c62a4SSepherosa Ziehau 
373089d55360SSepherosa Ziehau 	return pkts;
37318892ea20SAggelos Economopoulos }
37328892ea20SAggelos Economopoulos 
37338892ea20SAggelos Economopoulos static void
37348892ea20SAggelos Economopoulos mxge_tick(void *arg)
37358892ea20SAggelos Economopoulos {
37368892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
373789d55360SSepherosa Ziehau 	u_long pkts = 0;
37388892ea20SAggelos Economopoulos 	int err = 0;
3739ca8ca004SSepherosa Ziehau 	int ticks;
37408892ea20SAggelos Economopoulos 
374126634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&sc->main_serialize);
374289d55360SSepherosa Ziehau 
374389d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3744ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3745ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
374689d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3747ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3748ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
374989d55360SSepherosa Ziehau 	}
375089d55360SSepherosa Ziehau 	if (pkts == 0) {
3751cc9c62a4SSepherosa Ziehau 		uint16_t cmd;
3752cc9c62a4SSepherosa Ziehau 
3753ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
375489d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
375589d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
375689d55360SSepherosa Ziehau 			sc->dying = 2;
375726634ef8SSepherosa Ziehau 			mxge_serialize_skipmain(sc);
375889d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
375926634ef8SSepherosa Ziehau 			mxge_deserialize_skipmain(sc);
376089d55360SSepherosa Ziehau 			err = ENXIO;
376189d55360SSepherosa Ziehau 		}
3762cc9c62a4SSepherosa Ziehau 
3763ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
376489d55360SSepherosa Ziehau 		ticks *= 4;
376589d55360SSepherosa Ziehau 	}
376689d55360SSepherosa Ziehau 
37678892ea20SAggelos Economopoulos 	if (err == 0)
376889d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
376989d55360SSepherosa Ziehau 
377026634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&sc->main_serialize);
37718892ea20SAggelos Economopoulos }
37728892ea20SAggelos Economopoulos 
37738892ea20SAggelos Economopoulos static int
37748892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
37758892ea20SAggelos Economopoulos {
377600f2de12SSepherosa Ziehau 	mxge_softc_t *sc = ifp->if_softc;
377700f2de12SSepherosa Ziehau 	const struct ifmedia *ifm = &sc->media;
377800f2de12SSepherosa Ziehau 	int pause;
377900f2de12SSepherosa Ziehau 
378000f2de12SSepherosa Ziehau 	if (IFM_OPTIONS(ifm->ifm_media) & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE)) {
378100f2de12SSepherosa Ziehau 		if (sc->pause)
378200f2de12SSepherosa Ziehau 			return 0;
378300f2de12SSepherosa Ziehau 		pause = 1;
378400f2de12SSepherosa Ziehau 	} else {
378500f2de12SSepherosa Ziehau 		if (!sc->pause)
378600f2de12SSepherosa Ziehau 			return 0;
378700f2de12SSepherosa Ziehau 		pause = 0;
378800f2de12SSepherosa Ziehau 	}
378900f2de12SSepherosa Ziehau 	return mxge_change_pause(sc, pause);
37908892ea20SAggelos Economopoulos }
37918892ea20SAggelos Economopoulos 
37928892ea20SAggelos Economopoulos static int
37938892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
37948892ea20SAggelos Economopoulos {
37958892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
37968892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
37978892ea20SAggelos Economopoulos 	int err = 0;
37988892ea20SAggelos Economopoulos 
3799b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3800b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
38018892ea20SAggelos Economopoulos 		return EINVAL;
3802b9a8961fSSepherosa Ziehau 
38038892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
38048892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
38052ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
380689d55360SSepherosa Ziehau 		mxge_close(sc, 0);
38078892ea20SAggelos Economopoulos 		err = mxge_open(sc);
38088892ea20SAggelos Economopoulos 		if (err != 0) {
38098892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
381089d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3811b9a8961fSSepherosa Ziehau 			mxge_open(sc);
38128892ea20SAggelos Economopoulos 		}
38138892ea20SAggelos Economopoulos 	}
38148892ea20SAggelos Economopoulos 	return err;
38158892ea20SAggelos Economopoulos }
38168892ea20SAggelos Economopoulos 
38178892ea20SAggelos Economopoulos static void
38188892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
38198892ea20SAggelos Economopoulos {
38208892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38218892ea20SAggelos Economopoulos 
38228892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
3823166c46afSSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
3824166c46afSSepherosa Ziehau 
3825dbf0d419SSepherosa Ziehau 	if (sc->link_state)
3826166c46afSSepherosa Ziehau 		ifmr->ifm_status |= IFM_ACTIVE;
3827166c46afSSepherosa Ziehau 
3828dbf0d419SSepherosa Ziehau 	/*
3829dbf0d419SSepherosa Ziehau 	 * Autoselect is not supported, so the current media
3830dbf0d419SSepherosa Ziehau 	 * should be delivered.
3831dbf0d419SSepherosa Ziehau 	 */
383289d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
383300f2de12SSepherosa Ziehau 	if (sc->current_media != IFM_NONE) {
383400f2de12SSepherosa Ziehau 		ifmr->ifm_active |= MXGE_IFM;
383500f2de12SSepherosa Ziehau 		if (sc->pause)
383600f2de12SSepherosa Ziehau 			ifmr->ifm_active |= IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
383700f2de12SSepherosa Ziehau 	}
38388892ea20SAggelos Economopoulos }
38398892ea20SAggelos Economopoulos 
38408892ea20SAggelos Economopoulos static int
384189d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
384289d55360SSepherosa Ziehau     struct ucred *cr __unused)
38438892ea20SAggelos Economopoulos {
38448892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38458892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
38468892ea20SAggelos Economopoulos 	int err, mask;
38478892ea20SAggelos Economopoulos 
384826634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
3849af85d4d5SSepherosa Ziehau 	err = 0;
3850af85d4d5SSepherosa Ziehau 
38518892ea20SAggelos Economopoulos 	switch (command) {
38528892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
38538892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
38548892ea20SAggelos Economopoulos 		break;
38558892ea20SAggelos Economopoulos 
38568892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3857af85d4d5SSepherosa Ziehau 		if (sc->dying)
38588892ea20SAggelos Economopoulos 			return EINVAL;
3859af85d4d5SSepherosa Ziehau 
38608892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
38612ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
38628892ea20SAggelos Economopoulos 				err = mxge_open(sc);
38638892ea20SAggelos Economopoulos 			} else {
3864af85d4d5SSepherosa Ziehau 				/*
3865af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3866af85d4d5SSepherosa Ziehau 				 * flag changes
3867af85d4d5SSepherosa Ziehau 				 */
38688892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
38698892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
38708892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
38718892ea20SAggelos Economopoulos 			}
38728892ea20SAggelos Economopoulos 		} else {
3873af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
387489d55360SSepherosa Ziehau 				mxge_close(sc, 0);
38758892ea20SAggelos Economopoulos 		}
38768892ea20SAggelos Economopoulos 		break;
38778892ea20SAggelos Economopoulos 
38788892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
38798892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
38808892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
38818892ea20SAggelos Economopoulos 		break;
38828892ea20SAggelos Economopoulos 
38838892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
38848892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
38858892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
388689d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
388789d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
388889d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
38898892ea20SAggelos Economopoulos 			else
389089d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
38918892ea20SAggelos Economopoulos 		}
389289d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
389389d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
389489d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
389589d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
389689d55360SSepherosa Ziehau 			else
389789d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
389889d55360SSepherosa Ziehau 		}
389989d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
390089d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
39018892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
39028892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
39038892ea20SAggelos Economopoulos 		break;
39048892ea20SAggelos Economopoulos 
39058892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
390600f2de12SSepherosa Ziehau 	case SIOCSIFMEDIA:
39078892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
39088892ea20SAggelos Economopoulos 		    &sc->media, command);
39098892ea20SAggelos Economopoulos 		break;
39108892ea20SAggelos Economopoulos 
39118892ea20SAggelos Economopoulos 	default:
391289d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
391389d55360SSepherosa Ziehau 		break;
39148892ea20SAggelos Economopoulos 	}
39158892ea20SAggelos Economopoulos 	return err;
39168892ea20SAggelos Economopoulos }
39178892ea20SAggelos Economopoulos 
39188892ea20SAggelos Economopoulos static void
39198892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
39208892ea20SAggelos Economopoulos {
392100f2de12SSepherosa Ziehau 	int ifm;
392200f2de12SSepherosa Ziehau 
39237cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
39247cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
39257cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
39268892ea20SAggelos Economopoulos 
39277cc92483SSepherosa Ziehau 	/* XXX */
39288892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
39298892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
39307cc92483SSepherosa Ziehau 
393100f2de12SSepherosa Ziehau 	ifm = ifmedia_str2ethfc(mxge_flowctrl);
393200f2de12SSepherosa Ziehau 	if (ifm & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE))
393300f2de12SSepherosa Ziehau 		sc->pause = 1;
393400f2de12SSepherosa Ziehau 
39358433e5f5SSepherosa Ziehau 	sc->use_rss = mxge_use_rss;
39368892ea20SAggelos Economopoulos 
393789d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
39387cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
39397cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
39407cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
39417cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
394289d55360SSepherosa Ziehau }
39438892ea20SAggelos Economopoulos 
39448892ea20SAggelos Economopoulos static void
39458892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
39468892ea20SAggelos Economopoulos {
39478892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39488892ea20SAggelos Economopoulos 	int i;
39498892ea20SAggelos Economopoulos 
39508892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
39518892ea20SAggelos Economopoulos 		return;
39528892ea20SAggelos Economopoulos 
39538892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39548892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39558892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
39568892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
39578892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
39588892ea20SAggelos Economopoulos 		}
39599a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_done.entry != NULL) {
3960414caf0dSSepherosa Ziehau 			mxge_dma_free(&ss->rx_done_dma);
39619a4ae890SSepherosa Ziehau 			ss->rx_data.rx_done.entry = NULL;
39628892ea20SAggelos Economopoulos 		}
39638892ea20SAggelos Economopoulos 	}
39646c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
39658892ea20SAggelos Economopoulos 	sc->ss = NULL;
39668892ea20SAggelos Economopoulos }
39678892ea20SAggelos Economopoulos 
39688892ea20SAggelos Economopoulos static int
39698892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
39708892ea20SAggelos Economopoulos {
39718892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
39728892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39738892ea20SAggelos Economopoulos 	size_t bytes;
3974089301c2SSepherosa Ziehau 	int err, i, rx_ring_size;
39758892ea20SAggelos Economopoulos 
39768892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
39778892ea20SAggelos Economopoulos 	if (err != 0) {
39788892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
39798892ea20SAggelos Economopoulos 		return err;
39808892ea20SAggelos Economopoulos 	}
3981089301c2SSepherosa Ziehau 	rx_ring_size = cmd.data0;
3982089301c2SSepherosa Ziehau 	sc->rx_intr_slots = 2 * (rx_ring_size / sizeof (mcp_dma_addr_t));
39838892ea20SAggelos Economopoulos 
39848892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
3985dfda108aSSepherosa Ziehau 	sc->ss = kmalloc_cachealign(bytes, M_DEVBUF, M_WAITOK | M_ZERO);
39867cc92483SSepherosa Ziehau 
39878892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39888892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39898892ea20SAggelos Economopoulos 
39908892ea20SAggelos Economopoulos 		ss->sc = sc;
39918892ea20SAggelos Economopoulos 
399226634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->rx_data.rx_serialize);
399326634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->tx.tx_serialize);
3994e6c7b753SSepherosa Ziehau 		ss->intr_rid = -1;
399526634ef8SSepherosa Ziehau 
39967cc92483SSepherosa Ziehau 		/*
3997e6c7b753SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queue
3998089301c2SSepherosa Ziehau 		 * XXX assume 4bytes mcp_slot
39997cc92483SSepherosa Ziehau 		 */
4000089301c2SSepherosa Ziehau 		bytes = sc->rx_intr_slots * sizeof(mcp_slot_t);
4001414caf0dSSepherosa Ziehau 		err = mxge_dma_alloc(sc, &ss->rx_done_dma, bytes, 4096);
4002798c3369SSepherosa Ziehau 		if (err != 0) {
4003798c3369SSepherosa Ziehau 			device_printf(sc->dev,
4004798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
4005798c3369SSepherosa Ziehau 			return err;
4006798c3369SSepherosa Ziehau 		}
4007414caf0dSSepherosa Ziehau 		ss->rx_data.rx_done.entry = ss->rx_done_dma.dmem_addr;
40088892ea20SAggelos Economopoulos 
40098892ea20SAggelos Economopoulos 		/*
4010ebe934eaSSepherosa Ziehau 		 * Allocate the per-slice firmware stats
40118892ea20SAggelos Economopoulos 		 */
40128892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
40138892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
40148892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
4015798c3369SSepherosa Ziehau 		if (err != 0) {
4016798c3369SSepherosa Ziehau 			device_printf(sc->dev,
4017798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
4018798c3369SSepherosa Ziehau 			return err;
4019798c3369SSepherosa Ziehau 		}
40207cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
40218892ea20SAggelos Economopoulos 	}
40227cc92483SSepherosa Ziehau 	return 0;
40238892ea20SAggelos Economopoulos }
40248892ea20SAggelos Economopoulos 
40258892ea20SAggelos Economopoulos static void
40268892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
40278892ea20SAggelos Economopoulos {
4028e6c7b753SSepherosa Ziehau 	int status, max_intr_slots, max_slices, num_slices;
4029*bdbc20adSSepherosa Ziehau 	int msix_cnt, msix_enable, multi_tx;
40308892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4031c7431c78SSepherosa Ziehau 	const char *old_fw;
40328892ea20SAggelos Economopoulos 
40338892ea20SAggelos Economopoulos 	sc->num_slices = 1;
4034aca8f373SSepherosa Ziehau 	sc->num_tx_rings = 1;
40357cc92483SSepherosa Ziehau 
4036e6c7b753SSepherosa Ziehau 	num_slices = device_getenv_int(sc->dev, "num_slices", mxge_num_slices);
4037e6c7b753SSepherosa Ziehau 	if (num_slices == 1)
40388892ea20SAggelos Economopoulos 		return;
40398892ea20SAggelos Economopoulos 
4040*bdbc20adSSepherosa Ziehau 	if (netisr_ncpus == 1)
4041e6c7b753SSepherosa Ziehau 		return;
4042e6c7b753SSepherosa Ziehau 
4043e6c7b753SSepherosa Ziehau 	msix_enable = device_getenv_int(sc->dev, "msix.enable",
4044e6c7b753SSepherosa Ziehau 	    mxge_msix_enable);
4045e6c7b753SSepherosa Ziehau 	if (!msix_enable)
4046e6c7b753SSepherosa Ziehau 		return;
4047e6c7b753SSepherosa Ziehau 
40488892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
40498892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
40508892ea20SAggelos Economopoulos 		return;
4051*bdbc20adSSepherosa Ziehau 	if (bootverbose)
4052*bdbc20adSSepherosa Ziehau 		device_printf(sc->dev, "MSI-X count %d\n", msix_cnt);
4053e6c7b753SSepherosa Ziehau 
4054e6c7b753SSepherosa Ziehau 	/*
4055e6c7b753SSepherosa Ziehau 	 * Now load the slice aware firmware see what it supports
4056e6c7b753SSepherosa Ziehau 	 */
40578892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
40588892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
40598892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
40608892ea20SAggelos Economopoulos 	else
40618892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
40628892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
40638892ea20SAggelos Economopoulos 	if (status != 0) {
40648892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
40658892ea20SAggelos Economopoulos 		return;
40668892ea20SAggelos Economopoulos 	}
40678892ea20SAggelos Economopoulos 
4068e6c7b753SSepherosa Ziehau 	/*
4069e6c7b753SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it is alive
4070e6c7b753SSepherosa Ziehau 	 */
40718892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof(cmd));
40728892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
40738892ea20SAggelos Economopoulos 	if (status != 0) {
40748892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
40758892ea20SAggelos Economopoulos 		goto abort_with_fw;
40768892ea20SAggelos Economopoulos 	}
40778892ea20SAggelos Economopoulos 
4078e6c7b753SSepherosa Ziehau 	/*
4079e6c7b753SSepherosa Ziehau 	 * Get rx ring size to calculate rx interrupt queue size
4080e6c7b753SSepherosa Ziehau 	 */
40818892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40828892ea20SAggelos Economopoulos 	if (status != 0) {
40838892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
40848892ea20SAggelos Economopoulos 		goto abort_with_fw;
40858892ea20SAggelos Economopoulos 	}
40868892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof(mcp_dma_addr_t));
40878892ea20SAggelos Economopoulos 
4088e6c7b753SSepherosa Ziehau 	/*
4089e6c7b753SSepherosa Ziehau 	 * Tell it the size of the rx interrupt queue
4090e6c7b753SSepherosa Ziehau 	 */
40918892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof(struct mcp_slot);
40928892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
40938892ea20SAggelos Economopoulos 	if (status != 0) {
40948892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
40958892ea20SAggelos Economopoulos 		goto abort_with_fw;
40968892ea20SAggelos Economopoulos 	}
40978892ea20SAggelos Economopoulos 
4098e6c7b753SSepherosa Ziehau 	/*
4099e6c7b753SSepherosa Ziehau 	 * Ask the maximum number of slices it supports
4100e6c7b753SSepherosa Ziehau 	 */
41018892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
41028892ea20SAggelos Economopoulos 	if (status != 0) {
41038892ea20SAggelos Economopoulos 		device_printf(sc->dev,
41048892ea20SAggelos Economopoulos 		    "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
41058892ea20SAggelos Economopoulos 		goto abort_with_fw;
41068892ea20SAggelos Economopoulos 	}
4107e6c7b753SSepherosa Ziehau 	max_slices = cmd.data0;
4108*bdbc20adSSepherosa Ziehau 	if (bootverbose)
4109*bdbc20adSSepherosa Ziehau 		device_printf(sc->dev, "max slices %d\n", max_slices);
4110e6c7b753SSepherosa Ziehau 
4111e6c7b753SSepherosa Ziehau 	if (max_slices > msix_cnt)
4112e6c7b753SSepherosa Ziehau 		max_slices = msix_cnt;
4113e6c7b753SSepherosa Ziehau 
4114*bdbc20adSSepherosa Ziehau 	sc->ring_map = if_ringmap_alloc(sc->dev, num_slices, max_slices);
4115*bdbc20adSSepherosa Ziehau 	sc->num_slices = if_ringmap_count(sc->ring_map);
4116e6c7b753SSepherosa Ziehau 
4117aca8f373SSepherosa Ziehau 	multi_tx = device_getenv_int(sc->dev, "multi_tx", mxge_multi_tx);
4118aca8f373SSepherosa Ziehau 	if (multi_tx)
4119aca8f373SSepherosa Ziehau 		sc->num_tx_rings = sc->num_slices;
4120aca8f373SSepherosa Ziehau 
4121e6c7b753SSepherosa Ziehau 	if (bootverbose) {
4122e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "using %d slices, max %d\n",
4123e6c7b753SSepherosa Ziehau 		    sc->num_slices, max_slices);
41248892ea20SAggelos Economopoulos 	}
41258892ea20SAggelos Economopoulos 
4126e6c7b753SSepherosa Ziehau 	if (sc->num_slices == 1)
4127e6c7b753SSepherosa Ziehau 		goto abort_with_fw;
41288892ea20SAggelos Economopoulos 	return;
41298892ea20SAggelos Economopoulos 
41308892ea20SAggelos Economopoulos abort_with_fw:
41318892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
4132e6c7b753SSepherosa Ziehau 	mxge_load_firmware(sc, 0);
41338892ea20SAggelos Economopoulos }
41348892ea20SAggelos Economopoulos 
413526634ef8SSepherosa Ziehau static void
413626634ef8SSepherosa Ziehau mxge_setup_serialize(struct mxge_softc *sc)
413726634ef8SSepherosa Ziehau {
413826634ef8SSepherosa Ziehau 	int i = 0, slice;
413926634ef8SSepherosa Ziehau 
414026634ef8SSepherosa Ziehau 	/* Main + rx + tx */
414126634ef8SSepherosa Ziehau 	sc->nserialize = (2 * sc->num_slices) + 1;
414226634ef8SSepherosa Ziehau 	sc->serializes =
414326634ef8SSepherosa Ziehau 	    kmalloc(sc->nserialize * sizeof(struct lwkt_serialize *),
414426634ef8SSepherosa Ziehau 	        M_DEVBUF, M_WAITOK | M_ZERO);
414526634ef8SSepherosa Ziehau 
414626634ef8SSepherosa Ziehau 	/*
414726634ef8SSepherosa Ziehau 	 * Setup serializes
414826634ef8SSepherosa Ziehau 	 *
414926634ef8SSepherosa Ziehau 	 * NOTE: Order is critical
415026634ef8SSepherosa Ziehau 	 */
415126634ef8SSepherosa Ziehau 
415226634ef8SSepherosa Ziehau 	KKASSERT(i < sc->nserialize);
415326634ef8SSepherosa Ziehau 	sc->serializes[i++] = &sc->main_serialize;
415426634ef8SSepherosa Ziehau 
415526634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
415626634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
415726634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].rx_data.rx_serialize;
415826634ef8SSepherosa Ziehau 	}
415926634ef8SSepherosa Ziehau 
416026634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
416126634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
416226634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].tx.tx_serialize;
416326634ef8SSepherosa Ziehau 	}
416426634ef8SSepherosa Ziehau 
416526634ef8SSepherosa Ziehau 	KKASSERT(i == sc->nserialize);
416626634ef8SSepherosa Ziehau }
416726634ef8SSepherosa Ziehau 
416826634ef8SSepherosa Ziehau static void
416926634ef8SSepherosa Ziehau mxge_serialize(struct ifnet *ifp, enum ifnet_serialize slz)
417026634ef8SSepherosa Ziehau {
417126634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
417226634ef8SSepherosa Ziehau 
417326634ef8SSepherosa Ziehau 	ifnet_serialize_array_enter(sc->serializes, sc->nserialize, slz);
417426634ef8SSepherosa Ziehau }
417526634ef8SSepherosa Ziehau 
417626634ef8SSepherosa Ziehau static void
417726634ef8SSepherosa Ziehau mxge_deserialize(struct ifnet *ifp, enum ifnet_serialize slz)
417826634ef8SSepherosa Ziehau {
417926634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
418026634ef8SSepherosa Ziehau 
418126634ef8SSepherosa Ziehau 	ifnet_serialize_array_exit(sc->serializes, sc->nserialize, slz);
418226634ef8SSepherosa Ziehau }
418326634ef8SSepherosa Ziehau 
418426634ef8SSepherosa Ziehau static int
418526634ef8SSepherosa Ziehau mxge_tryserialize(struct ifnet *ifp, enum ifnet_serialize slz)
418626634ef8SSepherosa Ziehau {
418726634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
418826634ef8SSepherosa Ziehau 
418926634ef8SSepherosa Ziehau 	return ifnet_serialize_array_try(sc->serializes, sc->nserialize, slz);
419026634ef8SSepherosa Ziehau }
419126634ef8SSepherosa Ziehau 
419226634ef8SSepherosa Ziehau #ifdef INVARIANTS
419326634ef8SSepherosa Ziehau 
419426634ef8SSepherosa Ziehau static void
419526634ef8SSepherosa Ziehau mxge_serialize_assert(struct ifnet *ifp, enum ifnet_serialize slz,
419626634ef8SSepherosa Ziehau     boolean_t serialized)
419726634ef8SSepherosa Ziehau {
419826634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
419926634ef8SSepherosa Ziehau 
420026634ef8SSepherosa Ziehau 	ifnet_serialize_array_assert(sc->serializes, sc->nserialize,
420126634ef8SSepherosa Ziehau 	    slz, serialized);
420226634ef8SSepherosa Ziehau }
420326634ef8SSepherosa Ziehau 
420426634ef8SSepherosa Ziehau #endif	/* INVARIANTS */
420526634ef8SSepherosa Ziehau 
42062276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
42072276707eSSepherosa Ziehau 
42082276707eSSepherosa Ziehau static void
42092276707eSSepherosa Ziehau mxge_npoll_rx(struct ifnet *ifp, void *xss, int cycle)
42102276707eSSepherosa Ziehau {
42112276707eSSepherosa Ziehau 	struct mxge_slice_state *ss = xss;
42122276707eSSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
42132276707eSSepherosa Ziehau 
42142276707eSSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
42152276707eSSepherosa Ziehau 
42162276707eSSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0) {
42172276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, cycle);
42182276707eSSepherosa Ziehau 	} else {
42192276707eSSepherosa Ziehau 		/*
42202276707eSSepherosa Ziehau 		 * XXX
42212276707eSSepherosa Ziehau 		 * This register writting obviously has cost,
42222276707eSSepherosa Ziehau 		 * however, if we don't hand back the rx token,
42232276707eSSepherosa Ziehau 		 * the upcoming packets may suffer rediculously
42242276707eSSepherosa Ziehau 		 * large delay, as observed on 8AL-C using ping(8).
42252276707eSSepherosa Ziehau 		 */
42262276707eSSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
42272276707eSSepherosa Ziehau 	}
42282276707eSSepherosa Ziehau }
42292276707eSSepherosa Ziehau 
42302276707eSSepherosa Ziehau static void
42312276707eSSepherosa Ziehau mxge_npoll(struct ifnet *ifp, struct ifpoll_info *info)
42322276707eSSepherosa Ziehau {
42332276707eSSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
42342276707eSSepherosa Ziehau 	int i;
42352276707eSSepherosa Ziehau 
42362276707eSSepherosa Ziehau 	if (info == NULL)
42372276707eSSepherosa Ziehau 		return;
42382276707eSSepherosa Ziehau 
42392276707eSSepherosa Ziehau 	/*
42402276707eSSepherosa Ziehau 	 * Only poll rx; polling tx and status don't seem to work
42412276707eSSepherosa Ziehau 	 */
42422276707eSSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
42432276707eSSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4244*bdbc20adSSepherosa Ziehau 		int cpu = ss->intr_cpuid;
42452276707eSSepherosa Ziehau 
4246*bdbc20adSSepherosa Ziehau 		KKASSERT(cpu < netisr_ncpus);
4247*bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].poll_func = mxge_npoll_rx;
4248*bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].arg = ss;
4249*bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].serializer = &ss->rx_data.rx_serialize;
42502276707eSSepherosa Ziehau 	}
42512276707eSSepherosa Ziehau }
42522276707eSSepherosa Ziehau 
42532276707eSSepherosa Ziehau #endif	/* IFPOLL_ENABLE */
42542276707eSSepherosa Ziehau 
42558892ea20SAggelos Economopoulos static int
42568892ea20SAggelos Economopoulos mxge_attach(device_t dev)
42578892ea20SAggelos Economopoulos {
42588892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4259137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
4260aca8f373SSepherosa Ziehau 	int err, rid, i;
42618892ea20SAggelos Economopoulos 
4262f0115d64SAggelos Economopoulos 	/*
42637cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4264f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4265f0115d64SAggelos Economopoulos 	 */
4266f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
42678892ea20SAggelos Economopoulos 	sc->dev = dev;
42687cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
426900f2de12SSepherosa Ziehau 
427000f2de12SSepherosa Ziehau 	/* IFM_ETH_FORCEPAUSE can't be changed */
427100f2de12SSepherosa Ziehau 	ifmedia_init(&sc->media, IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE,
427200f2de12SSepherosa Ziehau 	    mxge_media_change, mxge_media_status);
42737cc92483SSepherosa Ziehau 
427426634ef8SSepherosa Ziehau 	lwkt_serialize_init(&sc->main_serialize);
427526634ef8SSepherosa Ziehau 
42768892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
42778892ea20SAggelos Economopoulos 
42788892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
42798892ea20SAggelos Economopoulos 				 1,			/* alignment */
42808892ea20SAggelos Economopoulos 				 0,			/* boundary */
42818892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
42828892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
42838892ea20SAggelos Economopoulos 				 NULL, NULL,		/* filter */
42847cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
42857cc92483SSepherosa Ziehau 				 0, 			/* num segs */
42867cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
42878892ea20SAggelos Economopoulos 				 0,			/* flags */
42888892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
42898892ea20SAggelos Economopoulos 	if (err != 0) {
4290798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4291798c3369SSepherosa Ziehau 		goto failed;
42928892ea20SAggelos Economopoulos 	}
42938892ea20SAggelos Economopoulos 
4294e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
42958892ea20SAggelos Economopoulos 
42968892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
42978892ea20SAggelos Economopoulos 
42987cc92483SSepherosa Ziehau 	/*
42997cc92483SSepherosa Ziehau 	 * Map the board into the kernel
43007cc92483SSepherosa Ziehau 	 */
43018892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
43027cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
43037cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
43048892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
43058892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
43068892ea20SAggelos Economopoulos 		err = ENXIO;
4307798c3369SSepherosa Ziehau 		goto failed;
43088892ea20SAggelos Economopoulos 	}
43097cc92483SSepherosa Ziehau 
43108892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
43118892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
43128892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
43138892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
43148892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
43158892ea20SAggelos Economopoulos 		err = ENXIO;
4316798c3369SSepherosa Ziehau 		goto failed;
43178892ea20SAggelos Economopoulos 	}
43188892ea20SAggelos Economopoulos 
43197cc92483SSepherosa Ziehau 	/*
43207cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
43217cc92483SSepherosa Ziehau 	 * lanai SRAM
43227cc92483SSepherosa Ziehau 	 */
43238892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
43248892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
43258892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
43268892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
43277cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
43288892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4329798c3369SSepherosa Ziehau 	if (err != 0) {
4330798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4331798c3369SSepherosa Ziehau 		goto failed;
4332798c3369SSepherosa Ziehau 	}
43338892ea20SAggelos Economopoulos 
43347cc92483SSepherosa Ziehau 	/*
43357cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
43367cc92483SSepherosa Ziehau 	 */
43378892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
43388892ea20SAggelos Economopoulos 
43397cc92483SSepherosa Ziehau 	/*
43407cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
43417cc92483SSepherosa Ziehau 	 */
43427cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4343798c3369SSepherosa Ziehau 	if (err != 0) {
4344798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4345798c3369SSepherosa Ziehau 		goto failed;
4346798c3369SSepherosa Ziehau 	}
43477cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
43487cc92483SSepherosa Ziehau 
43498892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4350798c3369SSepherosa Ziehau 	if (err != 0) {
4351798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4352798c3369SSepherosa Ziehau 		goto failed;
4353798c3369SSepherosa Ziehau 	}
43548892ea20SAggelos Economopoulos 
43558892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4356798c3369SSepherosa Ziehau 	if (err != 0) {
4357798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4358798c3369SSepherosa Ziehau 		goto failed;
4359798c3369SSepherosa Ziehau 	}
43608892ea20SAggelos Economopoulos 
43617cc92483SSepherosa Ziehau 	/* Select & load the firmware */
43628892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4363798c3369SSepherosa Ziehau 	if (err != 0) {
4364798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4365798c3369SSepherosa Ziehau 		goto failed;
4366798c3369SSepherosa Ziehau 	}
43678892ea20SAggelos Economopoulos 
43688892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
43698892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4370798c3369SSepherosa Ziehau 	if (err != 0) {
4371798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4372798c3369SSepherosa Ziehau 		goto failed;
4373798c3369SSepherosa Ziehau 	}
43748892ea20SAggelos Economopoulos 
4375e6c7b753SSepherosa Ziehau 	err = mxge_alloc_intr(sc);
4376e6c7b753SSepherosa Ziehau 	if (err != 0) {
4377e6c7b753SSepherosa Ziehau 		device_printf(dev, "alloc intr failed\n");
4378e6c7b753SSepherosa Ziehau 		goto failed;
4379e6c7b753SSepherosa Ziehau 	}
4380e6c7b753SSepherosa Ziehau 
438126634ef8SSepherosa Ziehau 	/* Setup serializes */
438226634ef8SSepherosa Ziehau 	mxge_setup_serialize(sc);
438326634ef8SSepherosa Ziehau 
43848892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4385798c3369SSepherosa Ziehau 	if (err != 0) {
4386798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4387798c3369SSepherosa Ziehau 		goto failed;
4388798c3369SSepherosa Ziehau 	}
43898892ea20SAggelos Economopoulos 
43908892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
43918892ea20SAggelos Economopoulos 	if (err != 0) {
4392798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4393798c3369SSepherosa Ziehau 		goto failed;
43948892ea20SAggelos Economopoulos 	}
43958892ea20SAggelos Economopoulos 
43968892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
439789d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
43988892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
439989d55360SSepherosa Ziehau 
440089d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
440189d55360SSepherosa Ziehau #if 0
440289d55360SSepherosa Ziehau 	/* Well, its software, sigh */
440389d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
440489d55360SSepherosa Ziehau #endif
44058892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
440689d55360SSepherosa Ziehau 
44078892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
44088892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
440989d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
44108892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
44118892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
44122276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
44132276707eSSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_LEGACY)
44142276707eSSepherosa Ziehau 		ifp->if_npoll = mxge_npoll;
44152276707eSSepherosa Ziehau #endif
441626634ef8SSepherosa Ziehau 	ifp->if_serialize = mxge_serialize;
441726634ef8SSepherosa Ziehau 	ifp->if_deserialize = mxge_deserialize;
441826634ef8SSepherosa Ziehau 	ifp->if_tryserialize = mxge_tryserialize;
441926634ef8SSepherosa Ziehau #ifdef INVARIANTS
442026634ef8SSepherosa Ziehau 	ifp->if_serialize_assert = mxge_serialize_assert;
442126634ef8SSepherosa Ziehau #endif
442289d55360SSepherosa Ziehau 
4423820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4424820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4425820e213fSSepherosa Ziehau 
44268892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
442789d55360SSepherosa Ziehau 	mxge_media_init(sc);
44288892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
442989d55360SSepherosa Ziehau 
4430cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
443189d55360SSepherosa Ziehau 
4432aca8f373SSepherosa Ziehau 	/* Setup TX rings and subqueues */
4433aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
4434aca8f373SSepherosa Ziehau 		struct ifaltq_subque *ifsq = ifq_get_subq(&ifp->if_snd, i);
4435aca8f373SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4436aca8f373SSepherosa Ziehau 
4437aca8f373SSepherosa Ziehau 		ifsq_set_cpuid(ifsq, ss->intr_cpuid);
4438aca8f373SSepherosa Ziehau 		ifsq_set_hw_serialize(ifsq, &ss->tx.tx_serialize);
4439aca8f373SSepherosa Ziehau 		ifsq_set_priv(ifsq, &ss->tx);
4440aca8f373SSepherosa Ziehau 		ss->tx.ifsq = ifsq;
4441aca8f373SSepherosa Ziehau 
4442aca8f373SSepherosa Ziehau 		ifsq_watchdog_init(&ss->tx.watchdog, ifsq, mxge_watchdog);
4443aca8f373SSepherosa Ziehau 	}
4444aca8f373SSepherosa Ziehau 
4445b9a8961fSSepherosa Ziehau 	/*
4446b9a8961fSSepherosa Ziehau 	 * XXX
4447b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4448b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4449b9a8961fSSepherosa Ziehau 	 */
4450b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4451b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
445289d55360SSepherosa Ziehau 	sc->dying = 0;
445389d55360SSepherosa Ziehau 
4454e6c7b753SSepherosa Ziehau 	err = mxge_setup_intr(sc);
4455369c353eSAggelos Economopoulos 	if (err != 0) {
4456798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4457798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4458798c3369SSepherosa Ziehau 		goto failed;
4459369c353eSAggelos Economopoulos 	}
446026634ef8SSepherosa Ziehau 
44618892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
446289d55360SSepherosa Ziehau 
446314929979SSepherosa Ziehau 	/* Increase non-cluster mbuf limit; used by small RX rings */
446414929979SSepherosa Ziehau 	mb_inclimit(ifp->if_nmbclusters);
446514929979SSepherosa Ziehau 
4466c9317e74SSepherosa Ziehau 	callout_reset_bycpu(&sc->co_hdl, mxge_ticks, mxge_tick, sc,
4467e6c7b753SSepherosa Ziehau 	    sc->ss[0].intr_cpuid);
44688892ea20SAggelos Economopoulos 	return 0;
44698892ea20SAggelos Economopoulos 
4470798c3369SSepherosa Ziehau failed:
4471798c3369SSepherosa Ziehau 	mxge_detach(dev);
44728892ea20SAggelos Economopoulos 	return err;
44738892ea20SAggelos Economopoulos }
44748892ea20SAggelos Economopoulos 
44758892ea20SAggelos Economopoulos static int
44768892ea20SAggelos Economopoulos mxge_detach(device_t dev)
44778892ea20SAggelos Economopoulos {
44788892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
44798892ea20SAggelos Economopoulos 
4480798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4481798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
448214929979SSepherosa Ziehau 		int mblimit = ifp->if_nmbclusters;
4483798c3369SSepherosa Ziehau 
448426634ef8SSepherosa Ziehau 		ifnet_serialize_all(ifp);
4485798c3369SSepherosa Ziehau 
44868892ea20SAggelos Economopoulos 		sc->dying = 1;
4487798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
448889d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4489e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4490798c3369SSepherosa Ziehau 
4491e6c7b753SSepherosa Ziehau 		mxge_teardown_intr(sc, sc->num_slices);
4492798c3369SSepherosa Ziehau 
449326634ef8SSepherosa Ziehau 		ifnet_deserialize_all(ifp);
4494e3dc37faSAggelos Economopoulos 
449589d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
449689d55360SSepherosa Ziehau 
4497798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
449814929979SSepherosa Ziehau 
449914929979SSepherosa Ziehau 		/* Decrease non-cluster mbuf limit increased by us */
450014929979SSepherosa Ziehau 		mb_inclimit(-mblimit);
4501798c3369SSepherosa Ziehau 	}
45028892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4503798c3369SSepherosa Ziehau 
4504798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4505798c3369SSepherosa Ziehau 	    sc->sram != NULL)
45068892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4507798c3369SSepherosa Ziehau 
4508e6c7b753SSepherosa Ziehau 	mxge_free_intr(sc);
45098892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
45108892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4511798c3369SSepherosa Ziehau 
4512e6c7b753SSepherosa Ziehau 	/* MUST after sysctls, intr and rings are freed */
45138892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4514798c3369SSepherosa Ziehau 
4515798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
45168892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4517798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
45188892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4519798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
45208892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4521798c3369SSepherosa Ziehau 
4522e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res != NULL) {
4523e6c7b753SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(2),
4524e6c7b753SSepherosa Ziehau 		    sc->msix_table_res);
4525798c3369SSepherosa Ziehau 	}
4526798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4527798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4528798c3369SSepherosa Ziehau 		    sc->mem_res);
4529798c3369SSepherosa Ziehau 	}
4530798c3369SSepherosa Ziehau 
4531798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
45328892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4533798c3369SSepherosa Ziehau 
4534*bdbc20adSSepherosa Ziehau 	if (sc->ring_map != NULL)
4535*bdbc20adSSepherosa Ziehau 		if_ringmap_free(sc->ring_map);
4536*bdbc20adSSepherosa Ziehau 
45378892ea20SAggelos Economopoulos 	return 0;
45388892ea20SAggelos Economopoulos }
45398892ea20SAggelos Economopoulos 
45408892ea20SAggelos Economopoulos static int
45418892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
45428892ea20SAggelos Economopoulos {
45438892ea20SAggelos Economopoulos 	return 0;
45448892ea20SAggelos Economopoulos }
4545e6c7b753SSepherosa Ziehau 
4546e6c7b753SSepherosa Ziehau static void
4547e6c7b753SSepherosa Ziehau mxge_free_msix(struct mxge_softc *sc, boolean_t setup)
4548e6c7b753SSepherosa Ziehau {
4549e6c7b753SSepherosa Ziehau 	int i;
4550e6c7b753SSepherosa Ziehau 
4551e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4552e6c7b753SSepherosa Ziehau 
4553e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4554e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4555e6c7b753SSepherosa Ziehau 
4556e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4557e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4558e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4559e6c7b753SSepherosa Ziehau 		}
4560e6c7b753SSepherosa Ziehau 		if (ss->intr_rid >= 0)
4561e6c7b753SSepherosa Ziehau 			pci_release_msix_vector(sc->dev, ss->intr_rid);
4562e6c7b753SSepherosa Ziehau 	}
4563e6c7b753SSepherosa Ziehau 	if (setup)
4564e6c7b753SSepherosa Ziehau 		pci_teardown_msix(sc->dev);
4565e6c7b753SSepherosa Ziehau }
4566e6c7b753SSepherosa Ziehau 
4567e6c7b753SSepherosa Ziehau static int
4568e6c7b753SSepherosa Ziehau mxge_alloc_msix(struct mxge_softc *sc)
4569e6c7b753SSepherosa Ziehau {
4570e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4571*bdbc20adSSepherosa Ziehau 	int rid, error, i;
4572e6c7b753SSepherosa Ziehau 	boolean_t setup = FALSE;
4573e6c7b753SSepherosa Ziehau 
4574e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4575e6c7b753SSepherosa Ziehau 
4576e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4577e6c7b753SSepherosa Ziehau 
4578e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4579e6c7b753SSepherosa Ziehau 	ss->intr_func = mxge_msi;
4580e6c7b753SSepherosa Ziehau 	ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4581e6c7b753SSepherosa Ziehau 	    "%s comb", device_get_nameunit(sc->dev));
4582e6c7b753SSepherosa Ziehau 	ss->intr_desc = ss->intr_desc0;
4583*bdbc20adSSepherosa Ziehau 	ss->intr_cpuid = if_ringmap_cpumap(sc->ring_map, 0);
4584e6c7b753SSepherosa Ziehau 
4585e6c7b753SSepherosa Ziehau 	for (i = 1; i < sc->num_slices; ++i) {
4586e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4587e6c7b753SSepherosa Ziehau 
4588e6c7b753SSepherosa Ziehau 		ss->intr_serialize = &ss->rx_data.rx_serialize;
4589aca8f373SSepherosa Ziehau 		if (sc->num_tx_rings == 1) {
4590e6c7b753SSepherosa Ziehau 			ss->intr_func = mxge_msix_rx;
4591e6c7b753SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4592*bdbc20adSSepherosa Ziehau 			    "%s rx%d", device_get_nameunit(sc->dev), i);
4593aca8f373SSepherosa Ziehau 		} else {
4594aca8f373SSepherosa Ziehau 			ss->intr_func = mxge_msix_rxtx;
4595aca8f373SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4596*bdbc20adSSepherosa Ziehau 			    "%s rxtx%d", device_get_nameunit(sc->dev), i);
4597aca8f373SSepherosa Ziehau 		}
4598e6c7b753SSepherosa Ziehau 		ss->intr_desc = ss->intr_desc0;
4599*bdbc20adSSepherosa Ziehau 		ss->intr_cpuid = if_ringmap_cpumap(sc->ring_map, i);
4600e6c7b753SSepherosa Ziehau 	}
4601e6c7b753SSepherosa Ziehau 
4602e6c7b753SSepherosa Ziehau 	rid = PCIR_BAR(2);
4603e6c7b753SSepherosa Ziehau 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
4604e6c7b753SSepherosa Ziehau 	    &rid, RF_ACTIVE);
4605e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res == NULL) {
4606e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "couldn't alloc MSI-X table res\n");
4607e6c7b753SSepherosa Ziehau 		return ENXIO;
4608e6c7b753SSepherosa Ziehau 	}
4609e6c7b753SSepherosa Ziehau 
4610e6c7b753SSepherosa Ziehau 	error = pci_setup_msix(sc->dev);
4611e6c7b753SSepherosa Ziehau 	if (error) {
4612e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not setup MSI-X\n");
4613e6c7b753SSepherosa Ziehau 		goto back;
4614e6c7b753SSepherosa Ziehau 	}
4615e6c7b753SSepherosa Ziehau 	setup = TRUE;
4616e6c7b753SSepherosa Ziehau 
4617e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4618e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4619e6c7b753SSepherosa Ziehau 
4620e6c7b753SSepherosa Ziehau 		error = pci_alloc_msix_vector(sc->dev, i, &ss->intr_rid,
4621e6c7b753SSepherosa Ziehau 		    ss->intr_cpuid);
4622e6c7b753SSepherosa Ziehau 		if (error) {
4623e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4624e6c7b753SSepherosa Ziehau 			    "MSI-X %d on cpu%d\n", i, ss->intr_cpuid);
4625e6c7b753SSepherosa Ziehau 			goto back;
4626e6c7b753SSepherosa Ziehau 		}
4627e6c7b753SSepherosa Ziehau 
4628e6c7b753SSepherosa Ziehau 		ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4629e6c7b753SSepherosa Ziehau 		    &ss->intr_rid, RF_ACTIVE);
4630e6c7b753SSepherosa Ziehau 		if (ss->intr_res == NULL) {
4631e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4632e6c7b753SSepherosa Ziehau 			    "MSI-X %d resource\n", i);
4633e6c7b753SSepherosa Ziehau 			error = ENXIO;
4634e6c7b753SSepherosa Ziehau 			goto back;
4635e6c7b753SSepherosa Ziehau 		}
4636e6c7b753SSepherosa Ziehau 	}
4637e6c7b753SSepherosa Ziehau 
4638e6c7b753SSepherosa Ziehau 	pci_enable_msix(sc->dev);
4639e6c7b753SSepherosa Ziehau 	sc->intr_type = PCI_INTR_TYPE_MSIX;
4640e6c7b753SSepherosa Ziehau back:
4641e6c7b753SSepherosa Ziehau 	if (error)
4642e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, setup);
4643e6c7b753SSepherosa Ziehau 	return error;
4644e6c7b753SSepherosa Ziehau }
4645e6c7b753SSepherosa Ziehau 
4646e6c7b753SSepherosa Ziehau static int
4647e6c7b753SSepherosa Ziehau mxge_alloc_intr(struct mxge_softc *sc)
4648e6c7b753SSepherosa Ziehau {
4649e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4650e6c7b753SSepherosa Ziehau 	u_int irq_flags;
4651e6c7b753SSepherosa Ziehau 
4652e6c7b753SSepherosa Ziehau 	if (sc->num_slices > 1) {
4653e6c7b753SSepherosa Ziehau 		int error;
4654e6c7b753SSepherosa Ziehau 
4655e6c7b753SSepherosa Ziehau 		error = mxge_alloc_msix(sc);
4656e6c7b753SSepherosa Ziehau 		if (error)
4657e6c7b753SSepherosa Ziehau 			return error;
4658e6c7b753SSepherosa Ziehau 		KKASSERT(sc->intr_type == PCI_INTR_TYPE_MSIX);
4659e6c7b753SSepherosa Ziehau 		return 0;
4660e6c7b753SSepherosa Ziehau 	}
4661e6c7b753SSepherosa Ziehau 
4662e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4663e6c7b753SSepherosa Ziehau 
4664e6c7b753SSepherosa Ziehau 	sc->intr_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
4665e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, &irq_flags);
4666e6c7b753SSepherosa Ziehau 
4667e6c7b753SSepherosa Ziehau 	ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4668e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, irq_flags);
4669e6c7b753SSepherosa Ziehau 	if (ss->intr_res == NULL) {
4670e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not alloc interrupt\n");
4671e6c7b753SSepherosa Ziehau 		return ENXIO;
4672e6c7b753SSepherosa Ziehau 	}
4673e6c7b753SSepherosa Ziehau 
4674e6c7b753SSepherosa Ziehau 	if (sc->intr_type == PCI_INTR_TYPE_LEGACY)
4675e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_legacy;
4676e6c7b753SSepherosa Ziehau 	else
4677e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_msi;
4678e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4679e6c7b753SSepherosa Ziehau 	ss->intr_cpuid = rman_get_cpuid(ss->intr_res);
4680e6c7b753SSepherosa Ziehau 
4681e6c7b753SSepherosa Ziehau 	return 0;
4682e6c7b753SSepherosa Ziehau }
4683e6c7b753SSepherosa Ziehau 
4684e6c7b753SSepherosa Ziehau static int
4685e6c7b753SSepherosa Ziehau mxge_setup_intr(struct mxge_softc *sc)
4686e6c7b753SSepherosa Ziehau {
4687e6c7b753SSepherosa Ziehau 	int i;
4688e6c7b753SSepherosa Ziehau 
4689e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4690e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4691e6c7b753SSepherosa Ziehau 		int error;
4692e6c7b753SSepherosa Ziehau 
4693e6c7b753SSepherosa Ziehau 		error = bus_setup_intr_descr(sc->dev, ss->intr_res,
4694e6c7b753SSepherosa Ziehau 		    INTR_MPSAFE, ss->intr_func, ss, &ss->intr_hand,
4695e6c7b753SSepherosa Ziehau 		    ss->intr_serialize, ss->intr_desc);
4696e6c7b753SSepherosa Ziehau 		if (error) {
4697e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "can't setup %dth intr\n", i);
4698e6c7b753SSepherosa Ziehau 			mxge_teardown_intr(sc, i);
4699e6c7b753SSepherosa Ziehau 			return error;
4700e6c7b753SSepherosa Ziehau 		}
4701e6c7b753SSepherosa Ziehau 	}
4702e6c7b753SSepherosa Ziehau 	return 0;
4703e6c7b753SSepherosa Ziehau }
4704e6c7b753SSepherosa Ziehau 
4705e6c7b753SSepherosa Ziehau static void
4706e6c7b753SSepherosa Ziehau mxge_teardown_intr(struct mxge_softc *sc, int cnt)
4707e6c7b753SSepherosa Ziehau {
4708e6c7b753SSepherosa Ziehau 	int i;
4709e6c7b753SSepherosa Ziehau 
4710e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4711e6c7b753SSepherosa Ziehau 		return;
4712e6c7b753SSepherosa Ziehau 
4713e6c7b753SSepherosa Ziehau 	for (i = 0; i < cnt; ++i) {
4714e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4715e6c7b753SSepherosa Ziehau 
4716e6c7b753SSepherosa Ziehau 		bus_teardown_intr(sc->dev, ss->intr_res, ss->intr_hand);
4717e6c7b753SSepherosa Ziehau 	}
4718e6c7b753SSepherosa Ziehau }
4719e6c7b753SSepherosa Ziehau 
4720e6c7b753SSepherosa Ziehau static void
4721e6c7b753SSepherosa Ziehau mxge_free_intr(struct mxge_softc *sc)
4722e6c7b753SSepherosa Ziehau {
4723e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4724e6c7b753SSepherosa Ziehau 		return;
4725e6c7b753SSepherosa Ziehau 
4726e6c7b753SSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_MSIX) {
4727e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[0];
4728e6c7b753SSepherosa Ziehau 
4729e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4730e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4731e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4732e6c7b753SSepherosa Ziehau 		}
4733e6c7b753SSepherosa Ziehau 		if (sc->intr_type == PCI_INTR_TYPE_MSI)
4734e6c7b753SSepherosa Ziehau 			pci_release_msi(sc->dev);
4735e6c7b753SSepherosa Ziehau 	} else {
4736e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, TRUE);
4737e6c7b753SSepherosa Ziehau 	}
4738e6c7b753SSepherosa Ziehau }
4739