xref: /dragonfly/sys/dev/netif/mxge/if_mxge.c (revision 030b0c8c)
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>
53afc5d5f3SSepherosa Ziehau #include <net/if_ringmap.h>
548892ea20SAggelos Economopoulos #include <net/ethernet.h>
558892ea20SAggelos Economopoulos #include <net/if_dl.h>
568892ea20SAggelos Economopoulos #include <net/if_media.h>
572276707eSSepherosa Ziehau #include <net/if_poll.h>
588892ea20SAggelos Economopoulos 
598892ea20SAggelos Economopoulos #include <net/bpf.h>
608892ea20SAggelos Economopoulos 
618892ea20SAggelos Economopoulos #include <net/if_types.h>
62b3535a6fSAggelos Economopoulos #include <net/vlan/if_vlan_var.h>
638892ea20SAggelos Economopoulos #include <net/zlib.h>
641cd61a7cSSepherosa Ziehau #include <net/toeplitz.h>
658892ea20SAggelos Economopoulos 
668892ea20SAggelos Economopoulos #include <netinet/in_systm.h>
678892ea20SAggelos Economopoulos #include <netinet/in.h>
688892ea20SAggelos Economopoulos #include <netinet/ip.h>
698892ea20SAggelos Economopoulos #include <netinet/tcp.h>
708892ea20SAggelos Economopoulos 
718892ea20SAggelos Economopoulos #include <sys/bus.h>
728892ea20SAggelos Economopoulos #include <sys/rman.h>
738892ea20SAggelos Economopoulos 
74b3535a6fSAggelos Economopoulos #include <bus/pci/pcireg.h>
75b3535a6fSAggelos Economopoulos #include <bus/pci/pcivar.h>
76b3535a6fSAggelos Economopoulos #include <bus/pci/pci_private.h> /* XXX for pci_cfg_restore */
778892ea20SAggelos Economopoulos 
788892ea20SAggelos Economopoulos #include <vm/vm.h>		/* for pmap_mapdev() */
798892ea20SAggelos Economopoulos #include <vm/pmap.h>
808892ea20SAggelos Economopoulos 
8170f95ad1SSascha Wildner #if defined(__x86_64__)
828892ea20SAggelos Economopoulos #include <machine/specialreg.h>
838892ea20SAggelos Economopoulos #endif
848892ea20SAggelos Economopoulos 
85b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mxge_mcp.h>
86b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/mcp_gen_header.h>
87b3535a6fSAggelos Economopoulos #include <dev/netif/mxge/if_mxge_var.h>
888892ea20SAggelos Economopoulos 
8900f2de12SSepherosa Ziehau #define MXGE_IFM	(IFM_ETHER | IFM_FDX | IFM_ETH_FORCEPAUSE)
9000f2de12SSepherosa Ziehau 
9160f8c66dSSepherosa Ziehau #define MXGE_RX_SMALL_BUFLEN		(MHLEN - MXGEFW_PAD)
921cd61a7cSSepherosa Ziehau #define MXGE_HWRSS_KEYLEN		16
9360f8c66dSSepherosa Ziehau 
948433e5f5SSepherosa Ziehau /* Tunable params */
958892ea20SAggelos Economopoulos static int mxge_nvidia_ecrc_enable = 1;
968892ea20SAggelos Economopoulos static int mxge_force_firmware = 0;
977cc92483SSepherosa Ziehau static int mxge_intr_coal_delay = MXGE_INTR_COAL_DELAY;
988892ea20SAggelos Economopoulos static int mxge_deassert_wait = 1;
998892ea20SAggelos Economopoulos static int mxge_ticks;
10038b8e77fSSepherosa Ziehau static int mxge_num_slices = 0;
1018892ea20SAggelos Economopoulos static int mxge_always_promisc = 0;
10289d55360SSepherosa Ziehau static int mxge_throttle = 0;
1037cc92483SSepherosa Ziehau static int mxge_msi_enable = 1;
104e6c7b753SSepherosa Ziehau static int mxge_msix_enable = 1;
105aca8f373SSepherosa Ziehau static int mxge_multi_tx = 1;
1068433e5f5SSepherosa Ziehau /*
1078433e5f5SSepherosa Ziehau  * Don't use RSS by default, its just too slow
1088433e5f5SSepherosa Ziehau  */
1098433e5f5SSepherosa Ziehau static int mxge_use_rss = 0;
1107cc92483SSepherosa Ziehau 
11117039ae7SSepherosa Ziehau static char mxge_flowctrl[IFM_ETH_FC_STRLEN] = IFM_ETH_FC_FORCE_NONE;
11200f2de12SSepherosa Ziehau 
113c7431c78SSepherosa Ziehau static const char *mxge_fw_unaligned = "mxge_ethp_z8e";
114c7431c78SSepherosa Ziehau static const char *mxge_fw_aligned = "mxge_eth_z8e";
115c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e";
116c7431c78SSepherosa Ziehau static const char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e";
1178892ea20SAggelos Economopoulos 
118e6c7b753SSepherosa Ziehau TUNABLE_INT("hw.mxge.num_slices", &mxge_num_slices);
1197cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.intr_coal_delay", &mxge_intr_coal_delay);
1207cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.nvidia_ecrc_enable", &mxge_nvidia_ecrc_enable);
1217cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.force_firmware", &mxge_force_firmware);
1227cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.deassert_wait", &mxge_deassert_wait);
1237cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.ticks", &mxge_ticks);
1247cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.always_promisc", &mxge_always_promisc);
1257cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.throttle", &mxge_throttle);
126aca8f373SSepherosa Ziehau TUNABLE_INT("hw.mxge.multi_tx", &mxge_multi_tx);
1278433e5f5SSepherosa Ziehau TUNABLE_INT("hw.mxge.use_rss", &mxge_use_rss);
1287cc92483SSepherosa Ziehau TUNABLE_INT("hw.mxge.msi.enable", &mxge_msi_enable);
129e6c7b753SSepherosa Ziehau TUNABLE_INT("hw.mxge.msix.enable", &mxge_msix_enable);
13000f2de12SSepherosa Ziehau TUNABLE_STR("hw.mxge.flow_ctrl", mxge_flowctrl, sizeof(mxge_flowctrl));
1317cc92483SSepherosa Ziehau 
1328892ea20SAggelos Economopoulos static int mxge_probe(device_t dev);
1338892ea20SAggelos Economopoulos static int mxge_attach(device_t dev);
1348892ea20SAggelos Economopoulos static int mxge_detach(device_t dev);
1358892ea20SAggelos Economopoulos static int mxge_shutdown(device_t dev);
1368892ea20SAggelos Economopoulos 
137e6c7b753SSepherosa Ziehau static int mxge_alloc_intr(struct mxge_softc *sc);
138e6c7b753SSepherosa Ziehau static void mxge_free_intr(struct mxge_softc *sc);
139e6c7b753SSepherosa Ziehau static int mxge_setup_intr(struct mxge_softc *sc);
140e6c7b753SSepherosa Ziehau static void mxge_teardown_intr(struct mxge_softc *sc, int cnt);
141e6c7b753SSepherosa Ziehau 
14289d55360SSepherosa Ziehau static device_method_t mxge_methods[] = {
1438892ea20SAggelos Economopoulos 	/* Device interface */
1448892ea20SAggelos Economopoulos 	DEVMETHOD(device_probe, mxge_probe),
1458892ea20SAggelos Economopoulos 	DEVMETHOD(device_attach, mxge_attach),
1468892ea20SAggelos Economopoulos 	DEVMETHOD(device_detach, mxge_detach),
1478892ea20SAggelos Economopoulos 	DEVMETHOD(device_shutdown, mxge_shutdown),
148d3c9c58eSSascha Wildner 	DEVMETHOD_END
1498892ea20SAggelos Economopoulos };
1508892ea20SAggelos Economopoulos 
15189d55360SSepherosa Ziehau static driver_t mxge_driver = {
1528892ea20SAggelos Economopoulos 	"mxge",
1538892ea20SAggelos Economopoulos 	mxge_methods,
1548892ea20SAggelos Economopoulos 	sizeof(mxge_softc_t),
1558892ea20SAggelos Economopoulos };
1568892ea20SAggelos Economopoulos 
1578892ea20SAggelos Economopoulos static devclass_t mxge_devclass;
1588892ea20SAggelos Economopoulos 
1598892ea20SAggelos Economopoulos /* Declare ourselves to be a child of the PCI bus.*/
160aa2b9d05SSascha Wildner DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, NULL, NULL);
1618892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, firmware, 1, 1, 1);
1628892ea20SAggelos Economopoulos MODULE_DEPEND(mxge, zlib, 1, 1, 1);
1638892ea20SAggelos Economopoulos 
1648892ea20SAggelos Economopoulos static int mxge_load_firmware(mxge_softc_t *sc, int adopt);
1658892ea20SAggelos Economopoulos static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data);
1662c29ffc6SSepherosa Ziehau static void mxge_close(mxge_softc_t *sc, int down);
1678892ea20SAggelos Economopoulos static int mxge_open(mxge_softc_t *sc);
1688892ea20SAggelos Economopoulos static void mxge_tick(void *arg);
169ca8ca004SSepherosa Ziehau static void mxge_watchdog_reset(mxge_softc_t *sc);
170ca8ca004SSepherosa Ziehau static void mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice);
1718892ea20SAggelos Economopoulos 
1728892ea20SAggelos Economopoulos static int
mxge_probe(device_t dev)1738892ea20SAggelos Economopoulos mxge_probe(device_t dev)
1748892ea20SAggelos Economopoulos {
17524e43b1cSSepherosa Ziehau 	if (pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM &&
17624e43b1cSSepherosa Ziehau 	    (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E ||
17724e43b1cSSepherosa Ziehau 	     pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9)) {
17824e43b1cSSepherosa Ziehau 		int rev = pci_get_revid(dev);
1798892ea20SAggelos Economopoulos 
1808892ea20SAggelos Economopoulos 		switch (rev) {
1818892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8E:
1828892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8A");
1838892ea20SAggelos Economopoulos 			break;
1848892ea20SAggelos Economopoulos 		case MXGE_PCI_REV_Z8ES:
1858892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8B");
1868892ea20SAggelos Economopoulos 			break;
1878892ea20SAggelos Economopoulos 		default:
1888892ea20SAggelos Economopoulos 			device_set_desc(dev, "Myri10G-PCIE-8??");
18924e43b1cSSepherosa Ziehau 			device_printf(dev, "Unrecognized rev %d NIC\n", rev);
1908892ea20SAggelos Economopoulos 			break;
1918892ea20SAggelos Economopoulos 		}
1928892ea20SAggelos Economopoulos 		return 0;
1938892ea20SAggelos Economopoulos 	}
1948892ea20SAggelos Economopoulos 	return ENXIO;
1958892ea20SAggelos Economopoulos }
1968892ea20SAggelos Economopoulos 
1978892ea20SAggelos Economopoulos static void
mxge_enable_wc(mxge_softc_t * sc)1988892ea20SAggelos Economopoulos mxge_enable_wc(mxge_softc_t *sc)
1998892ea20SAggelos Economopoulos {
20070f95ad1SSascha Wildner #if defined(__x86_64__)
2018892ea20SAggelos Economopoulos 	vm_offset_t len;
2028892ea20SAggelos Economopoulos 
2038892ea20SAggelos Economopoulos 	sc->wc = 1;
2048892ea20SAggelos Economopoulos 	len = rman_get_size(sc->mem_res);
20589d55360SSepherosa Ziehau 	pmap_change_attr((vm_offset_t) sc->sram, len / PAGE_SIZE,
20689d55360SSepherosa Ziehau 	    PAT_WRITE_COMBINING);
2079eb279beSAggelos Economopoulos #endif
2088892ea20SAggelos Economopoulos }
2098892ea20SAggelos Economopoulos 
2108892ea20SAggelos Economopoulos static int
mxge_dma_alloc(mxge_softc_t * sc,bus_dmamem_t * dma,size_t bytes,bus_size_t alignment)2117cc92483SSepherosa Ziehau mxge_dma_alloc(mxge_softc_t *sc, bus_dmamem_t *dma, size_t bytes,
2128892ea20SAggelos Economopoulos     bus_size_t alignment)
2138892ea20SAggelos Economopoulos {
2147cc92483SSepherosa Ziehau 	bus_size_t boundary;
2158892ea20SAggelos Economopoulos 	int err;
2168892ea20SAggelos Economopoulos 
2177cc92483SSepherosa Ziehau 	if (bytes > 4096 && alignment == 4096)
2188892ea20SAggelos Economopoulos 		boundary = 0;
2197cc92483SSepherosa Ziehau 	else
2208892ea20SAggelos Economopoulos 		boundary = 4096;
2218892ea20SAggelos Economopoulos 
2227cc92483SSepherosa Ziehau 	err = bus_dmamem_coherent(sc->parent_dmat, alignment, boundary,
2237cc92483SSepherosa Ziehau 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, bytes,
2247cc92483SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, dma);
2258892ea20SAggelos Economopoulos 	if (err != 0) {
2267cc92483SSepherosa Ziehau 		device_printf(sc->dev, "bus_dmamem_coherent failed: %d\n", err);
2278892ea20SAggelos Economopoulos 		return err;
2288892ea20SAggelos Economopoulos 	}
2298892ea20SAggelos Economopoulos 	return 0;
2308892ea20SAggelos Economopoulos }
2318892ea20SAggelos Economopoulos 
2328892ea20SAggelos Economopoulos static void
mxge_dma_free(bus_dmamem_t * dma)2337cc92483SSepherosa Ziehau mxge_dma_free(bus_dmamem_t *dma)
2348892ea20SAggelos Economopoulos {
2357cc92483SSepherosa Ziehau 	bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
2367cc92483SSepherosa Ziehau 	bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
2377cc92483SSepherosa Ziehau 	bus_dma_tag_destroy(dma->dmem_tag);
2388892ea20SAggelos Economopoulos }
2398892ea20SAggelos Economopoulos 
2408892ea20SAggelos Economopoulos /*
2418892ea20SAggelos Economopoulos  * The eeprom strings on the lanaiX have the format
2428892ea20SAggelos Economopoulos  * SN=x\0
2438892ea20SAggelos Economopoulos  * MAC=x:x:x:x:x:x\0
2448892ea20SAggelos Economopoulos  * PC=text\0
2458892ea20SAggelos Economopoulos  */
2468892ea20SAggelos Economopoulos static int
mxge_parse_strings(mxge_softc_t * sc)2478892ea20SAggelos Economopoulos mxge_parse_strings(mxge_softc_t *sc)
2488892ea20SAggelos Economopoulos {
249c7431c78SSepherosa Ziehau 	const char *ptr;
25089d55360SSepherosa Ziehau 	int i, found_mac, found_sn2;
25189d55360SSepherosa Ziehau 	char *endptr;
2528892ea20SAggelos Economopoulos 
2538892ea20SAggelos Economopoulos 	ptr = sc->eeprom_strings;
2548892ea20SAggelos Economopoulos 	found_mac = 0;
25589d55360SSepherosa Ziehau 	found_sn2 = 0;
25689d55360SSepherosa Ziehau 	while (*ptr != '\0') {
25789d55360SSepherosa Ziehau 		if (strncmp(ptr, "MAC=", 4) == 0) {
25889d55360SSepherosa Ziehau 			ptr += 4;
25989d55360SSepherosa Ziehau 			for (i = 0;;) {
26089d55360SSepherosa Ziehau 				sc->mac_addr[i] = strtoul(ptr, &endptr, 16);
26189d55360SSepherosa Ziehau 				if (endptr - ptr != 2)
2628892ea20SAggelos Economopoulos 					goto abort;
26389d55360SSepherosa Ziehau 				ptr = endptr;
26489d55360SSepherosa Ziehau 				if (++i == 6)
26589d55360SSepherosa Ziehau 					break;
26689d55360SSepherosa Ziehau 				if (*ptr++ != ':')
26789d55360SSepherosa Ziehau 					goto abort;
26889d55360SSepherosa Ziehau 			}
2698892ea20SAggelos Economopoulos 			found_mac = 1;
27089d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "PC=", 3) == 0) {
2718892ea20SAggelos Economopoulos 			ptr += 3;
27289d55360SSepherosa Ziehau 			strlcpy(sc->product_code_string, ptr,
27389d55360SSepherosa Ziehau 			    sizeof(sc->product_code_string));
27489d55360SSepherosa Ziehau 		} else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) {
2758892ea20SAggelos Economopoulos 			ptr += 3;
27689d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
27789d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
27889d55360SSepherosa Ziehau 		} else if (strncmp(ptr, "SN2=", 4) == 0) {
27989d55360SSepherosa Ziehau 			/* SN2 takes precedence over SN */
28089d55360SSepherosa Ziehau 			ptr += 4;
28189d55360SSepherosa Ziehau 			found_sn2 = 1;
28289d55360SSepherosa Ziehau 			strlcpy(sc->serial_number_string, ptr,
28389d55360SSepherosa Ziehau 			    sizeof(sc->serial_number_string));
2848892ea20SAggelos Economopoulos 		}
28589d55360SSepherosa Ziehau 		while (*ptr++ != '\0') {}
2868892ea20SAggelos Economopoulos 	}
2878892ea20SAggelos Economopoulos 
2888892ea20SAggelos Economopoulos 	if (found_mac)
2898892ea20SAggelos Economopoulos 		return 0;
2908892ea20SAggelos Economopoulos 
2918892ea20SAggelos Economopoulos abort:
2928892ea20SAggelos Economopoulos 	device_printf(sc->dev, "failed to parse eeprom_strings\n");
2938892ea20SAggelos Economopoulos 	return ENXIO;
2948892ea20SAggelos Economopoulos }
2958892ea20SAggelos Economopoulos 
29670f95ad1SSascha Wildner #if defined(__x86_64__)
29789d55360SSepherosa Ziehau 
2988892ea20SAggelos Economopoulos static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)2998892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
3008892ea20SAggelos Economopoulos {
3018892ea20SAggelos Economopoulos 	uint32_t val;
3028892ea20SAggelos Economopoulos 	unsigned long base, off;
3038892ea20SAggelos Economopoulos 	char *va, *cfgptr;
3048892ea20SAggelos Economopoulos 	device_t pdev, mcp55;
3058892ea20SAggelos Economopoulos 	uint16_t vendor_id, device_id, word;
3068892ea20SAggelos Economopoulos 	uintptr_t bus, slot, func, ivend, idev;
3078892ea20SAggelos Economopoulos 	uint32_t *ptr32;
3088892ea20SAggelos Economopoulos 
3098892ea20SAggelos Economopoulos 	if (!mxge_nvidia_ecrc_enable)
3108892ea20SAggelos Economopoulos 		return;
3118892ea20SAggelos Economopoulos 
3128892ea20SAggelos Economopoulos 	pdev = device_get_parent(device_get_parent(sc->dev));
3138892ea20SAggelos Economopoulos 	if (pdev == NULL) {
3148892ea20SAggelos Economopoulos 		device_printf(sc->dev, "could not find parent?\n");
3158892ea20SAggelos Economopoulos 		return;
3168892ea20SAggelos Economopoulos 	}
3178892ea20SAggelos Economopoulos 	vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2);
3188892ea20SAggelos Economopoulos 	device_id = pci_read_config(pdev, PCIR_DEVICE, 2);
3198892ea20SAggelos Economopoulos 
3208892ea20SAggelos Economopoulos 	if (vendor_id != 0x10de)
3218892ea20SAggelos Economopoulos 		return;
3228892ea20SAggelos Economopoulos 
3238892ea20SAggelos Economopoulos 	base = 0;
3248892ea20SAggelos Economopoulos 
3258892ea20SAggelos Economopoulos 	if (device_id == 0x005d) {
3268892ea20SAggelos Economopoulos 		/* ck804, base address is magic */
3278892ea20SAggelos Economopoulos 		base = 0xe0000000UL;
3288892ea20SAggelos Economopoulos 	} else if (device_id >= 0x0374 && device_id <= 0x378) {
3298892ea20SAggelos Economopoulos 		/* mcp55, base address stored in chipset */
3308892ea20SAggelos Economopoulos 		mcp55 = pci_find_bsf(0, 0, 0);
3318892ea20SAggelos Economopoulos 		if (mcp55 &&
3328892ea20SAggelos Economopoulos 		    0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) &&
3338892ea20SAggelos Economopoulos 		    0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) {
3348892ea20SAggelos Economopoulos 			word = pci_read_config(mcp55, 0x90, 2);
3358892ea20SAggelos Economopoulos 			base = ((unsigned long)word & 0x7ffeU) << 25;
3368892ea20SAggelos Economopoulos 		}
3378892ea20SAggelos Economopoulos 	}
3388892ea20SAggelos Economopoulos 	if (!base)
3398892ea20SAggelos Economopoulos 		return;
3408892ea20SAggelos Economopoulos 
3417cc92483SSepherosa Ziehau 	/*
3427cc92483SSepherosa Ziehau 	 * XXXX
3437cc92483SSepherosa Ziehau 	 * Test below is commented because it is believed that doing
3447cc92483SSepherosa Ziehau 	 * config read/write beyond 0xff will access the config space
3457cc92483SSepherosa Ziehau 	 * for the next larger function.  Uncomment this and remove
3467cc92483SSepherosa Ziehau 	 * the hacky pmap_mapdev() way of accessing config space when
3472f47b54fSSepherosa Ziehau 	 * DragonFly grows support for extended pcie config space access.
3488892ea20SAggelos Economopoulos 	 */
3498892ea20SAggelos Economopoulos #if 0
3507cc92483SSepherosa Ziehau 	/*
3517cc92483SSepherosa Ziehau 	 * See if we can, by some miracle, access the extended
3527cc92483SSepherosa Ziehau 	 * config space
3537cc92483SSepherosa Ziehau 	 */
3548892ea20SAggelos Economopoulos 	val = pci_read_config(pdev, 0x178, 4);
3558892ea20SAggelos Economopoulos 	if (val != 0xffffffff) {
3568892ea20SAggelos Economopoulos 		val |= 0x40;
3578892ea20SAggelos Economopoulos 		pci_write_config(pdev, 0x178, val, 4);
3588892ea20SAggelos Economopoulos 		return;
3598892ea20SAggelos Economopoulos 	}
3608892ea20SAggelos Economopoulos #endif
3617cc92483SSepherosa Ziehau 	/*
3627cc92483SSepherosa Ziehau 	 * Rather than using normal pci config space writes, we must
3638892ea20SAggelos Economopoulos 	 * map the Nvidia config space ourselves.  This is because on
3648892ea20SAggelos Economopoulos 	 * opteron/nvidia class machine the 0xe000000 mapping is
3658892ea20SAggelos Economopoulos 	 * handled by the nvidia chipset, that means the internal PCI
3668892ea20SAggelos Economopoulos 	 * device (the on-chip northbridge), or the amd-8131 bridge
3678892ea20SAggelos Economopoulos 	 * and things behind them are not visible by this method.
3688892ea20SAggelos Economopoulos 	 */
3698892ea20SAggelos Economopoulos 
3708892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3718892ea20SAggelos Economopoulos 		      PCI_IVAR_BUS, &bus);
3728892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3738892ea20SAggelos Economopoulos 		      PCI_IVAR_SLOT, &slot);
3748892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3758892ea20SAggelos Economopoulos 		      PCI_IVAR_FUNCTION, &func);
3768892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3778892ea20SAggelos Economopoulos 		      PCI_IVAR_VENDOR, &ivend);
3788892ea20SAggelos Economopoulos 	BUS_READ_IVAR(device_get_parent(pdev), pdev,
3798892ea20SAggelos Economopoulos 		      PCI_IVAR_DEVICE, &idev);
3808892ea20SAggelos Economopoulos 
3817cc92483SSepherosa Ziehau 	off =  base + 0x00100000UL * (unsigned long)bus +
3827cc92483SSepherosa Ziehau 	    0x00001000UL * (unsigned long)(func + 8 * slot);
3838892ea20SAggelos Economopoulos 
3848892ea20SAggelos Economopoulos 	/* map it into the kernel */
3858892ea20SAggelos Economopoulos 	va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE);
3868892ea20SAggelos Economopoulos 	if (va == NULL) {
3878892ea20SAggelos Economopoulos 		device_printf(sc->dev, "pmap_kenter_temporary didn't\n");
3888892ea20SAggelos Economopoulos 		return;
3898892ea20SAggelos Economopoulos 	}
3908892ea20SAggelos Economopoulos 	/* get a pointer to the config space mapped into the kernel */
3918892ea20SAggelos Economopoulos 	cfgptr = va + (off & PAGE_MASK);
3928892ea20SAggelos Economopoulos 
3938892ea20SAggelos Economopoulos 	/* make sure that we can really access it */
3948892ea20SAggelos Economopoulos 	vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR);
3958892ea20SAggelos Economopoulos 	device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE);
3968892ea20SAggelos Economopoulos 	if (!(vendor_id == ivend && device_id == idev)) {
3978892ea20SAggelos Economopoulos 		device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n",
3988892ea20SAggelos Economopoulos 		    vendor_id, device_id);
3998892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4008892ea20SAggelos Economopoulos 		return;
4018892ea20SAggelos Economopoulos 	}
4028892ea20SAggelos Economopoulos 
4038892ea20SAggelos Economopoulos 	ptr32 = (uint32_t*)(cfgptr + 0x178);
4048892ea20SAggelos Economopoulos 	val = *ptr32;
4058892ea20SAggelos Economopoulos 
4068892ea20SAggelos Economopoulos 	if (val == 0xffffffff) {
4078892ea20SAggelos Economopoulos 		device_printf(sc->dev, "extended mapping failed\n");
4088892ea20SAggelos Economopoulos 		pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4098892ea20SAggelos Economopoulos 		return;
4108892ea20SAggelos Economopoulos 	}
4118892ea20SAggelos Economopoulos 	*ptr32 = val | 0x40;
4128892ea20SAggelos Economopoulos 	pmap_unmapdev((vm_offset_t)va, PAGE_SIZE);
4137cc92483SSepherosa Ziehau 	if (bootverbose) {
4147cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Enabled ECRC on upstream "
4157cc92483SSepherosa Ziehau 		    "Nvidia bridge at %d:%d:%d\n",
4168892ea20SAggelos Economopoulos 		    (int)bus, (int)slot, (int)func);
4177cc92483SSepherosa Ziehau 	}
4188892ea20SAggelos Economopoulos }
41989d55360SSepherosa Ziehau 
42070f95ad1SSascha Wildner #else	/* __x86_64__ */
42189d55360SSepherosa Ziehau 
4228892ea20SAggelos Economopoulos static void
mxge_enable_nvidia_ecrc(mxge_softc_t * sc)4238892ea20SAggelos Economopoulos mxge_enable_nvidia_ecrc(mxge_softc_t *sc)
4248892ea20SAggelos Economopoulos {
4257cc92483SSepherosa Ziehau 	device_printf(sc->dev, "Nforce 4 chipset on non-x86/x86_64!?!?!\n");
4268892ea20SAggelos Economopoulos }
4278892ea20SAggelos Economopoulos 
42889d55360SSepherosa Ziehau #endif
4298892ea20SAggelos Economopoulos 
4308892ea20SAggelos Economopoulos static int
mxge_dma_test(mxge_softc_t * sc,int test_type)4318892ea20SAggelos Economopoulos mxge_dma_test(mxge_softc_t *sc, int test_type)
4328892ea20SAggelos Economopoulos {
4338892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4347cc92483SSepherosa Ziehau 	bus_addr_t dmatest_bus = sc->dmabench_dma.dmem_busaddr;
4358892ea20SAggelos Economopoulos 	int status;
4368892ea20SAggelos Economopoulos 	uint32_t len;
4377cc92483SSepherosa Ziehau 	const char *test = " ";
4388892ea20SAggelos Economopoulos 
4397cc92483SSepherosa Ziehau 	/*
4407cc92483SSepherosa Ziehau 	 * Run a small DMA test.
4418892ea20SAggelos Economopoulos 	 * The magic multipliers to the length tell the firmware
4428892ea20SAggelos Economopoulos 	 * to do DMA read, write, or read+write tests.  The
4438892ea20SAggelos Economopoulos 	 * results are returned in cmd.data0.  The upper 16
4448892ea20SAggelos Economopoulos 	 * bits of the return is the number of transfers completed.
4458892ea20SAggelos Economopoulos 	 * The lower 16 bits is the time in 0.5us ticks that the
4468892ea20SAggelos Economopoulos 	 * transfers took to complete.
4478892ea20SAggelos Economopoulos 	 */
4488892ea20SAggelos Economopoulos 
4498892ea20SAggelos Economopoulos 	len = sc->tx_boundary;
4508892ea20SAggelos Economopoulos 
4518892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4528892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4538892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10000;
4548892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4558892ea20SAggelos Economopoulos 	if (status != 0) {
4568892ea20SAggelos Economopoulos 		test = "read";
4578892ea20SAggelos Economopoulos 		goto abort;
4588892ea20SAggelos Economopoulos 	}
4597cc92483SSepherosa Ziehau 	sc->read_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4607cc92483SSepherosa Ziehau 
4618892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4628892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4638892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x1;
4648892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4658892ea20SAggelos Economopoulos 	if (status != 0) {
4668892ea20SAggelos Economopoulos 		test = "write";
4678892ea20SAggelos Economopoulos 		goto abort;
4688892ea20SAggelos Economopoulos 	}
4697cc92483SSepherosa Ziehau 	sc->write_dma = ((cmd.data0>>16) * len * 2) / (cmd.data0 & 0xffff);
4708892ea20SAggelos Economopoulos 
4718892ea20SAggelos Economopoulos 	cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus);
4728892ea20SAggelos Economopoulos 	cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus);
4738892ea20SAggelos Economopoulos 	cmd.data2 = len * 0x10001;
4748892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, test_type, &cmd);
4758892ea20SAggelos Economopoulos 	if (status != 0) {
4768892ea20SAggelos Economopoulos 		test = "read/write";
4778892ea20SAggelos Economopoulos 		goto abort;
4788892ea20SAggelos Economopoulos 	}
4798892ea20SAggelos Economopoulos 	sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) /
4808892ea20SAggelos Economopoulos 	    (cmd.data0 & 0xffff);
4818892ea20SAggelos Economopoulos 
4828892ea20SAggelos Economopoulos abort:
4837cc92483SSepherosa Ziehau 	if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) {
4848892ea20SAggelos Economopoulos 		device_printf(sc->dev, "DMA %s benchmark failed: %d\n",
4858892ea20SAggelos Economopoulos 		    test, status);
4867cc92483SSepherosa Ziehau 	}
4878892ea20SAggelos Economopoulos 	return status;
4888892ea20SAggelos Economopoulos }
4898892ea20SAggelos Economopoulos 
4908892ea20SAggelos Economopoulos /*
4918892ea20SAggelos Economopoulos  * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
4928892ea20SAggelos Economopoulos  * when the PCI-E Completion packets are aligned on an 8-byte
4938892ea20SAggelos Economopoulos  * boundary.  Some PCI-E chip sets always align Completion packets; on
4948892ea20SAggelos Economopoulos  * the ones that do not, the alignment can be enforced by enabling
4958892ea20SAggelos Economopoulos  * ECRC generation (if supported).
4968892ea20SAggelos Economopoulos  *
4978892ea20SAggelos Economopoulos  * When PCI-E Completion packets are not aligned, it is actually more
4988892ea20SAggelos Economopoulos  * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
4998892ea20SAggelos Economopoulos  *
5008892ea20SAggelos Economopoulos  * If the driver can neither enable ECRC nor verify that it has
5018892ea20SAggelos Economopoulos  * already been enabled, then it must use a firmware image which works
5028892ea20SAggelos Economopoulos  * around unaligned completion packets (ethp_z8e.dat), and it should
5038892ea20SAggelos Economopoulos  * also ensure that it never gives the device a Read-DMA which is
5048892ea20SAggelos Economopoulos  * larger than 2KB by setting the tx_boundary to 2KB.  If ECRC is
5058892ea20SAggelos Economopoulos  * enabled, then the driver should use the aligned (eth_z8e.dat)
5068892ea20SAggelos Economopoulos  * firmware image, and set tx_boundary to 4KB.
5078892ea20SAggelos Economopoulos  */
5088892ea20SAggelos Economopoulos static int
mxge_firmware_probe(mxge_softc_t * sc)5098892ea20SAggelos Economopoulos mxge_firmware_probe(mxge_softc_t *sc)
5108892ea20SAggelos Economopoulos {
5118892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
5128892ea20SAggelos Economopoulos 	int reg, status;
5138892ea20SAggelos Economopoulos 	uint16_t pectl;
5148892ea20SAggelos Economopoulos 
5158892ea20SAggelos Economopoulos 	sc->tx_boundary = 4096;
5167cc92483SSepherosa Ziehau 
5178892ea20SAggelos Economopoulos 	/*
5188892ea20SAggelos Economopoulos 	 * Verify the max read request size was set to 4KB
5198892ea20SAggelos Economopoulos 	 * before trying the test with 4KB.
5208892ea20SAggelos Economopoulos 	 */
5218892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
5228892ea20SAggelos Economopoulos 		pectl = pci_read_config(dev, reg + 0x8, 2);
5238892ea20SAggelos Economopoulos 		if ((pectl & (5 << 12)) != (5 << 12)) {
5247cc92483SSepherosa Ziehau 			device_printf(dev, "Max Read Req. size != 4k (0x%x)\n",
5258892ea20SAggelos Economopoulos 			    pectl);
5268892ea20SAggelos Economopoulos 			sc->tx_boundary = 2048;
5278892ea20SAggelos Economopoulos 		}
5288892ea20SAggelos Economopoulos 	}
5298892ea20SAggelos Economopoulos 
5308892ea20SAggelos Economopoulos 	/*
5317cc92483SSepherosa Ziehau 	 * Load the optimized firmware (which assumes aligned PCIe
5328892ea20SAggelos Economopoulos 	 * completions) in order to see if it works on this host.
5338892ea20SAggelos Economopoulos 	 */
5348892ea20SAggelos Economopoulos 	sc->fw_name = mxge_fw_aligned;
5358892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 1);
5367cc92483SSepherosa Ziehau 	if (status != 0)
5378892ea20SAggelos Economopoulos 		return status;
5388892ea20SAggelos Economopoulos 
5398892ea20SAggelos Economopoulos 	/*
5408892ea20SAggelos Economopoulos 	 * Enable ECRC if possible
5418892ea20SAggelos Economopoulos 	 */
5428892ea20SAggelos Economopoulos 	mxge_enable_nvidia_ecrc(sc);
5438892ea20SAggelos Economopoulos 
5448892ea20SAggelos Economopoulos 	/*
5458892ea20SAggelos Economopoulos 	 * Run a DMA test which watches for unaligned completions and
54689d55360SSepherosa Ziehau 	 * aborts on the first one seen.  Not required on Z8ES or newer.
5478892ea20SAggelos Economopoulos 	 */
54889d55360SSepherosa Ziehau 	if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES)
54989d55360SSepherosa Ziehau 		return 0;
5508892ea20SAggelos Economopoulos 
5518892ea20SAggelos Economopoulos 	status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST);
5528892ea20SAggelos Economopoulos 	if (status == 0)
5538892ea20SAggelos Economopoulos 		return 0; /* keep the aligned firmware */
5548892ea20SAggelos Economopoulos 
5558892ea20SAggelos Economopoulos 	if (status != E2BIG)
5568892ea20SAggelos Economopoulos 		device_printf(dev, "DMA test failed: %d\n", status);
5577cc92483SSepherosa Ziehau 	if (status == ENOSYS) {
5588892ea20SAggelos Economopoulos 		device_printf(dev, "Falling back to ethp! "
5598892ea20SAggelos Economopoulos 		    "Please install up to date fw\n");
5607cc92483SSepherosa Ziehau 	}
5618892ea20SAggelos Economopoulos 	return status;
5628892ea20SAggelos Economopoulos }
5638892ea20SAggelos Economopoulos 
5648892ea20SAggelos Economopoulos static int
mxge_select_firmware(mxge_softc_t * sc)5658892ea20SAggelos Economopoulos mxge_select_firmware(mxge_softc_t *sc)
5668892ea20SAggelos Economopoulos {
5678892ea20SAggelos Economopoulos 	int aligned = 0;
56889d55360SSepherosa Ziehau 	int force_firmware = mxge_force_firmware;
5698892ea20SAggelos Economopoulos 
57089d55360SSepherosa Ziehau 	if (sc->throttle)
57189d55360SSepherosa Ziehau 		force_firmware = sc->throttle;
5728892ea20SAggelos Economopoulos 
57389d55360SSepherosa Ziehau 	if (force_firmware != 0) {
57489d55360SSepherosa Ziehau 		if (force_firmware == 1)
5758892ea20SAggelos Economopoulos 			aligned = 1;
5768892ea20SAggelos Economopoulos 		else
5778892ea20SAggelos Economopoulos 			aligned = 0;
5787cc92483SSepherosa Ziehau 		if (bootverbose) {
5798892ea20SAggelos Economopoulos 			device_printf(sc->dev,
5808892ea20SAggelos Economopoulos 			    "Assuming %s completions (forced)\n",
5818892ea20SAggelos Economopoulos 			    aligned ? "aligned" : "unaligned");
5827cc92483SSepherosa Ziehau 		}
5838892ea20SAggelos Economopoulos 		goto abort;
5848892ea20SAggelos Economopoulos 	}
5858892ea20SAggelos Economopoulos 
5867cc92483SSepherosa Ziehau 	/*
5877cc92483SSepherosa Ziehau 	 * If the PCIe link width is 4 or less, we can use the aligned
5887cc92483SSepherosa Ziehau 	 * firmware and skip any checks
5897cc92483SSepherosa Ziehau 	 */
5908892ea20SAggelos Economopoulos 	if (sc->link_width != 0 && sc->link_width <= 4) {
5917cc92483SSepherosa Ziehau 		device_printf(sc->dev, "PCIe x%d Link, "
5927cc92483SSepherosa Ziehau 		    "expect reduced performance\n", sc->link_width);
5938892ea20SAggelos Economopoulos 		aligned = 1;
5948892ea20SAggelos Economopoulos 		goto abort;
5958892ea20SAggelos Economopoulos 	}
5968892ea20SAggelos Economopoulos 
5977cc92483SSepherosa Ziehau 	if (mxge_firmware_probe(sc) == 0)
5988892ea20SAggelos Economopoulos 		return 0;
5998892ea20SAggelos Economopoulos 
6008892ea20SAggelos Economopoulos abort:
6018892ea20SAggelos Economopoulos 	if (aligned) {
6028892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_aligned;
6038892ea20SAggelos Economopoulos 		sc->tx_boundary = 4096;
6048892ea20SAggelos Economopoulos 	} else {
6058892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
6068892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
6078892ea20SAggelos Economopoulos 	}
6087cc92483SSepherosa Ziehau 	return mxge_load_firmware(sc, 0);
6098892ea20SAggelos Economopoulos }
6108892ea20SAggelos Economopoulos 
6118892ea20SAggelos Economopoulos static int
mxge_validate_firmware(mxge_softc_t * sc,const mcp_gen_header_t * hdr)6128892ea20SAggelos Economopoulos mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr)
6138892ea20SAggelos Economopoulos {
6148892ea20SAggelos Economopoulos 	if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) {
6158a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware type: 0x%x\n",
6168892ea20SAggelos Economopoulos 		    be32toh(hdr->mcp_type));
6178892ea20SAggelos Economopoulos 		return EIO;
6188892ea20SAggelos Economopoulos 	}
6198892ea20SAggelos Economopoulos 
6207cc92483SSepherosa Ziehau 	/* Save firmware version for sysctl */
6217cc92483SSepherosa Ziehau 	strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version));
6227cc92483SSepherosa Ziehau 	if (bootverbose)
6238a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "firmware id: %s\n", hdr->version);
6248892ea20SAggelos Economopoulos 
625b6670ba0SAggelos Economopoulos 	ksscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major,
6268892ea20SAggelos Economopoulos 	    &sc->fw_ver_minor, &sc->fw_ver_tiny);
6278892ea20SAggelos Economopoulos 
6287cc92483SSepherosa Ziehau 	if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR &&
6297cc92483SSepherosa Ziehau 	      sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
6308a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Found firmware version %s\n",
6318892ea20SAggelos Economopoulos 		    sc->fw_version);
6328a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Driver needs %d.%d\n",
6338892ea20SAggelos Economopoulos 		    MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR);
6348892ea20SAggelos Economopoulos 		return EINVAL;
6358892ea20SAggelos Economopoulos 	}
6368892ea20SAggelos Economopoulos 	return 0;
6378892ea20SAggelos Economopoulos }
6388892ea20SAggelos Economopoulos 
6398892ea20SAggelos Economopoulos static void *
z_alloc(void * nil,u_int items,u_int size)6408892ea20SAggelos Economopoulos z_alloc(void *nil, u_int items, u_int size)
6418892ea20SAggelos Economopoulos {
6427cc92483SSepherosa Ziehau 	return kmalloc(items * size, M_TEMP, M_WAITOK);
6438892ea20SAggelos Economopoulos }
6448892ea20SAggelos Economopoulos 
6458892ea20SAggelos Economopoulos static void
z_free(void * nil,void * ptr)6468892ea20SAggelos Economopoulos z_free(void *nil, void *ptr)
6478892ea20SAggelos Economopoulos {
648d777b84fSAggelos Economopoulos 	kfree(ptr, M_TEMP);
6498892ea20SAggelos Economopoulos }
650d83c779aSSascha Wildner 
6518892ea20SAggelos Economopoulos static int
mxge_load_firmware_helper(mxge_softc_t * sc,uint32_t * limit)6528892ea20SAggelos Economopoulos mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit)
6538892ea20SAggelos Economopoulos {
654d83c779aSSascha Wildner 	z_stream zs;
655d83c779aSSascha Wildner 	char *inflate_buffer;
656d83c779aSSascha Wildner 	const struct firmware *fw;
6578892ea20SAggelos Economopoulos 	const mcp_gen_header_t *hdr;
6588892ea20SAggelos Economopoulos 	unsigned hdr_offset;
6598892ea20SAggelos Economopoulos 	int status;
6608892ea20SAggelos Economopoulos 	unsigned int i;
66189d55360SSepherosa Ziehau 	char dummy;
6628892ea20SAggelos Economopoulos 	size_t fw_len;
6638892ea20SAggelos Economopoulos 
664d83c779aSSascha Wildner 	fw = firmware_get(sc->fw_name);
6658892ea20SAggelos Economopoulos 	if (fw == NULL) {
6668a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Could not find firmware image %s\n",
6678892ea20SAggelos Economopoulos 		    sc->fw_name);
6688892ea20SAggelos Economopoulos 		return ENOENT;
6698892ea20SAggelos Economopoulos 	}
670d83c779aSSascha Wildner 
6717cc92483SSepherosa Ziehau 	/* Setup zlib and decompress f/w */
6728892ea20SAggelos Economopoulos 	bzero(&zs, sizeof(zs));
6738892ea20SAggelos Economopoulos 	zs.zalloc = z_alloc;
6748892ea20SAggelos Economopoulos 	zs.zfree = z_free;
6758892ea20SAggelos Economopoulos 	status = inflateInit(&zs);
6768892ea20SAggelos Economopoulos 	if (status != Z_OK) {
6778892ea20SAggelos Economopoulos 		status = EIO;
6788892ea20SAggelos Economopoulos 		goto abort_with_fw;
6798892ea20SAggelos Economopoulos 	}
6808892ea20SAggelos Economopoulos 
6817cc92483SSepherosa Ziehau 	/*
6827cc92483SSepherosa Ziehau 	 * The uncompressed size is stored as the firmware version,
6837cc92483SSepherosa Ziehau 	 * which would otherwise go unused
6847cc92483SSepherosa Ziehau 	 */
6858892ea20SAggelos Economopoulos 	fw_len = (size_t)fw->version;
6867cc92483SSepherosa Ziehau 	inflate_buffer = kmalloc(fw_len, M_TEMP, M_WAITOK);
6878892ea20SAggelos Economopoulos 	zs.avail_in = fw->datasize;
6888892ea20SAggelos Economopoulos 	zs.next_in = __DECONST(char *, fw->data);
6898892ea20SAggelos Economopoulos 	zs.avail_out = fw_len;
6908892ea20SAggelos Economopoulos 	zs.next_out = inflate_buffer;
6918892ea20SAggelos Economopoulos 	status = inflate(&zs, Z_FINISH);
6928892ea20SAggelos Economopoulos 	if (status != Z_STREAM_END) {
6938a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "zlib %d\n", status);
6948892ea20SAggelos Economopoulos 		status = EIO;
6958892ea20SAggelos Economopoulos 		goto abort_with_buffer;
6968892ea20SAggelos Economopoulos 	}
697d83c779aSSascha Wildner 
6987cc92483SSepherosa Ziehau 	/* Check id */
6997cc92483SSepherosa Ziehau 	hdr_offset =
7007cc92483SSepherosa Ziehau 	htobe32(*(const uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET));
7018892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) {
7028a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Bad firmware file");
7038892ea20SAggelos Economopoulos 		status = EIO;
704d83c779aSSascha Wildner 		goto abort_with_buffer;
7058892ea20SAggelos Economopoulos 	}
706d83c779aSSascha Wildner 	hdr = (const void*)(inflate_buffer + hdr_offset);
7078892ea20SAggelos Economopoulos 
7088892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
7098892ea20SAggelos Economopoulos 	if (status != 0)
710d83c779aSSascha Wildner 		goto abort_with_buffer;
7118892ea20SAggelos Economopoulos 
7128892ea20SAggelos Economopoulos 	/* Copy the inflated firmware to NIC SRAM. */
7138892ea20SAggelos Economopoulos 	for (i = 0; i < fw_len; i += 256) {
7147cc92483SSepherosa Ziehau 		mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, inflate_buffer + i,
7158892ea20SAggelos Economopoulos 		    min(256U, (unsigned)(fw_len - i)));
7168892ea20SAggelos Economopoulos 		wmb();
71789d55360SSepherosa Ziehau 		dummy = *sc->sram;
7188892ea20SAggelos Economopoulos 		wmb();
7198892ea20SAggelos Economopoulos 	}
7208892ea20SAggelos Economopoulos 
7218892ea20SAggelos Economopoulos 	*limit = fw_len;
7228892ea20SAggelos Economopoulos 	status = 0;
7238892ea20SAggelos Economopoulos abort_with_buffer:
724d777b84fSAggelos Economopoulos 	kfree(inflate_buffer, M_TEMP);
7258892ea20SAggelos Economopoulos 	inflateEnd(&zs);
7268892ea20SAggelos Economopoulos abort_with_fw:
727d83c779aSSascha Wildner 	firmware_put(fw, FIRMWARE_UNLOAD);
7288892ea20SAggelos Economopoulos 	return status;
7298892ea20SAggelos Economopoulos }
7308892ea20SAggelos Economopoulos 
7318892ea20SAggelos Economopoulos /*
7328892ea20SAggelos Economopoulos  * Enable or disable periodic RDMAs from the host to make certain
7338892ea20SAggelos Economopoulos  * chipsets resend dropped PCIe messages
7348892ea20SAggelos Economopoulos  */
7358892ea20SAggelos Economopoulos static void
mxge_dummy_rdma(mxge_softc_t * sc,int enable)7368892ea20SAggelos Economopoulos mxge_dummy_rdma(mxge_softc_t *sc, int enable)
7378892ea20SAggelos Economopoulos {
7388892ea20SAggelos Economopoulos 	char buf_bytes[72];
7398892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
7408892ea20SAggelos Economopoulos 	volatile char *submit;
7418892ea20SAggelos Economopoulos 	uint32_t *buf, dma_low, dma_high;
7428892ea20SAggelos Economopoulos 	int i;
7438892ea20SAggelos Economopoulos 
7448892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7458892ea20SAggelos Economopoulos 
7467cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
7478892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
7488892ea20SAggelos Economopoulos 	*confirm = 0;
7498892ea20SAggelos Economopoulos 	wmb();
7508892ea20SAggelos Economopoulos 
7517cc92483SSepherosa Ziehau 	/*
7527cc92483SSepherosa Ziehau 	 * Send an rdma command to the PCIe engine, and wait for the
7537cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
7547cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
7558892ea20SAggelos Economopoulos 	 */
7567cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7577cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
7588892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);		/* confirm addr MSW */
7598892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);		/* confirm addr LSW */
7608892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);		/* confirm data */
7617cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7627cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.dmem_busaddr);
7638892ea20SAggelos Economopoulos 	buf[3] = htobe32(dma_high); 		/* dummy addr MSW */
7648892ea20SAggelos Economopoulos 	buf[4] = htobe32(dma_low); 		/* dummy addr LSW */
7658892ea20SAggelos Economopoulos 	buf[5] = htobe32(enable);		/* enable? */
7668892ea20SAggelos Economopoulos 
7678892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA);
7688892ea20SAggelos Economopoulos 
7698892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
7708892ea20SAggelos Economopoulos 	wmb();
7718892ea20SAggelos Economopoulos 	DELAY(1000);
7728892ea20SAggelos Economopoulos 	wmb();
7738892ea20SAggelos Economopoulos 	i = 0;
7748892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
7758892ea20SAggelos Economopoulos 		DELAY(1000);
7768892ea20SAggelos Economopoulos 		i++;
7778892ea20SAggelos Economopoulos 	}
7788892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
7796ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "dummy rdma %s failed (%p = 0x%x)",
7807cc92483SSepherosa Ziehau 		    (enable ? "enable" : "disable"), confirm, *confirm);
7818892ea20SAggelos Economopoulos 	}
7828892ea20SAggelos Economopoulos }
7838892ea20SAggelos Economopoulos 
7848892ea20SAggelos Economopoulos static int
mxge_send_cmd(mxge_softc_t * sc,uint32_t cmd,mxge_cmd_t * data)7858892ea20SAggelos Economopoulos mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data)
7868892ea20SAggelos Economopoulos {
7878892ea20SAggelos Economopoulos 	mcp_cmd_t *buf;
7888892ea20SAggelos Economopoulos 	char buf_bytes[sizeof(*buf) + 8];
7898892ea20SAggelos Economopoulos 	volatile mcp_cmd_response_t *response = sc->cmd;
7908892ea20SAggelos Economopoulos 	volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD;
7918892ea20SAggelos Economopoulos 	uint32_t dma_low, dma_high;
7928892ea20SAggelos Economopoulos 	int err, sleep_total = 0;
7938892ea20SAggelos Economopoulos 
7946ee6cba3SSepherosa Ziehau 	/* Ensure buf is aligned to 8 bytes */
7958892ea20SAggelos Economopoulos 	buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
7968892ea20SAggelos Economopoulos 
7978892ea20SAggelos Economopoulos 	buf->data0 = htobe32(data->data0);
7988892ea20SAggelos Economopoulos 	buf->data1 = htobe32(data->data1);
7998892ea20SAggelos Economopoulos 	buf->data2 = htobe32(data->data2);
8008892ea20SAggelos Economopoulos 	buf->cmd = htobe32(cmd);
8017cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
8027cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
8038892ea20SAggelos Economopoulos 
8048892ea20SAggelos Economopoulos 	buf->response_addr.low = htobe32(dma_low);
8058892ea20SAggelos Economopoulos 	buf->response_addr.high = htobe32(dma_high);
8062e8181d0SAggelos Economopoulos 
8078892ea20SAggelos Economopoulos 	response->result = 0xffffffff;
8088892ea20SAggelos Economopoulos 	wmb();
8098892ea20SAggelos Economopoulos 	mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf));
8108892ea20SAggelos Economopoulos 
8116ee6cba3SSepherosa Ziehau 	/*
8126ee6cba3SSepherosa Ziehau 	 * Wait up to 20ms
8136ee6cba3SSepherosa Ziehau 	 */
8148892ea20SAggelos Economopoulos 	err = EAGAIN;
8158892ea20SAggelos Economopoulos 	for (sleep_total = 0; sleep_total < 20; sleep_total++) {
8168892ea20SAggelos Economopoulos 		wmb();
8178892ea20SAggelos Economopoulos 		switch (be32toh(response->result)) {
8188892ea20SAggelos Economopoulos 		case 0:
8198892ea20SAggelos Economopoulos 			data->data0 = be32toh(response->data);
8208892ea20SAggelos Economopoulos 			err = 0;
8218892ea20SAggelos Economopoulos 			break;
8228892ea20SAggelos Economopoulos 		case 0xffffffff:
8238892ea20SAggelos Economopoulos 			DELAY(1000);
8248892ea20SAggelos Economopoulos 			break;
8258892ea20SAggelos Economopoulos 		case MXGEFW_CMD_UNKNOWN:
8268892ea20SAggelos Economopoulos 			err = ENOSYS;
8278892ea20SAggelos Economopoulos 			break;
8288892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_UNALIGNED:
8298892ea20SAggelos Economopoulos 			err = E2BIG;
8308892ea20SAggelos Economopoulos 			break;
8318892ea20SAggelos Economopoulos 		case MXGEFW_CMD_ERROR_BUSY:
8328892ea20SAggelos Economopoulos 			err = EBUSY;
8338892ea20SAggelos Economopoulos 			break;
83489d55360SSepherosa Ziehau 		case MXGEFW_CMD_ERROR_I2C_ABSENT:
83589d55360SSepherosa Ziehau 			err = ENXIO;
83689d55360SSepherosa Ziehau 			break;
8378892ea20SAggelos Economopoulos 		default:
8386ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "command %d failed, result = %d\n",
8398892ea20SAggelos Economopoulos 			    cmd, be32toh(response->result));
8408892ea20SAggelos Economopoulos 			err = ENXIO;
8418892ea20SAggelos Economopoulos 			break;
8428892ea20SAggelos Economopoulos 		}
8438892ea20SAggelos Economopoulos 		if (err != EAGAIN)
8448892ea20SAggelos Economopoulos 			break;
8458892ea20SAggelos Economopoulos 	}
8466ee6cba3SSepherosa Ziehau 	if (err == EAGAIN) {
8476ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "command %d timed out result = %d\n",
8488892ea20SAggelos Economopoulos 		    cmd, be32toh(response->result));
8496ee6cba3SSepherosa Ziehau 	}
8508892ea20SAggelos Economopoulos 	return err;
8518892ea20SAggelos Economopoulos }
8528892ea20SAggelos Economopoulos 
8538892ea20SAggelos Economopoulos static int
mxge_adopt_running_firmware(mxge_softc_t * sc)8548892ea20SAggelos Economopoulos mxge_adopt_running_firmware(mxge_softc_t *sc)
8558892ea20SAggelos Economopoulos {
8568892ea20SAggelos Economopoulos 	struct mcp_gen_header *hdr;
8578892ea20SAggelos Economopoulos 	const size_t bytes = sizeof(struct mcp_gen_header);
8588892ea20SAggelos Economopoulos 	size_t hdr_offset;
8598892ea20SAggelos Economopoulos 	int status;
8608892ea20SAggelos Economopoulos 
8617cc92483SSepherosa Ziehau 	/*
8627cc92483SSepherosa Ziehau 	 * Find running firmware header
8637cc92483SSepherosa Ziehau 	 */
8647cc92483SSepherosa Ziehau 	hdr_offset =
8657cc92483SSepherosa Ziehau 	htobe32(*(volatile uint32_t *)(sc->sram + MCP_HEADER_PTR_OFFSET));
8668892ea20SAggelos Economopoulos 
8678892ea20SAggelos Economopoulos 	if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) {
8688a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Running firmware has bad header offset "
8698a20b038SSepherosa Ziehau 		    "(%zu)\n", hdr_offset);
8708892ea20SAggelos Economopoulos 		return EIO;
8718892ea20SAggelos Economopoulos 	}
8728892ea20SAggelos Economopoulos 
8737cc92483SSepherosa Ziehau 	/*
8747cc92483SSepherosa Ziehau 	 * Copy header of running firmware from SRAM to host memory to
8757cc92483SSepherosa Ziehau 	 * validate firmware
8767cc92483SSepherosa Ziehau 	 */
8777cc92483SSepherosa Ziehau 	hdr = kmalloc(bytes, M_DEVBUF, M_WAITOK);
8788892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
8797cc92483SSepherosa Ziehau 	    rman_get_bushandle(sc->mem_res), hdr_offset, (char *)hdr, bytes);
8808892ea20SAggelos Economopoulos 	status = mxge_validate_firmware(sc, hdr);
881d777b84fSAggelos Economopoulos 	kfree(hdr, M_DEVBUF);
8828892ea20SAggelos Economopoulos 
8838892ea20SAggelos Economopoulos 	/*
8847cc92483SSepherosa Ziehau 	 * Check to see if adopted firmware has bug where adopting
8858892ea20SAggelos Economopoulos 	 * it will cause broadcasts to be filtered unless the NIC
8868892ea20SAggelos Economopoulos 	 * is kept in ALLMULTI mode
8878892ea20SAggelos Economopoulos 	 */
8888892ea20SAggelos Economopoulos 	if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 &&
8898892ea20SAggelos Economopoulos 	    sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) {
8908892ea20SAggelos Economopoulos 		sc->adopted_rx_filter_bug = 1;
8918a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Adopting fw %d.%d.%d: "
8928892ea20SAggelos Economopoulos 		    "working around rx filter bug\n",
8937cc92483SSepherosa Ziehau 		    sc->fw_ver_major, sc->fw_ver_minor, sc->fw_ver_tiny);
8948892ea20SAggelos Economopoulos 	}
8958892ea20SAggelos Economopoulos 
8968892ea20SAggelos Economopoulos 	return status;
8978892ea20SAggelos Economopoulos }
8988892ea20SAggelos Economopoulos 
8998892ea20SAggelos Economopoulos static int
mxge_load_firmware(mxge_softc_t * sc,int adopt)9008892ea20SAggelos Economopoulos mxge_load_firmware(mxge_softc_t *sc, int adopt)
9018892ea20SAggelos Economopoulos {
9028892ea20SAggelos Economopoulos 	volatile uint32_t *confirm;
9038892ea20SAggelos Economopoulos 	volatile char *submit;
9048892ea20SAggelos Economopoulos 	char buf_bytes[72];
9058892ea20SAggelos Economopoulos 	uint32_t *buf, size, dma_low, dma_high;
9068892ea20SAggelos Economopoulos 	int status, i;
9078892ea20SAggelos Economopoulos 
9088892ea20SAggelos Economopoulos 	buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL);
9098892ea20SAggelos Economopoulos 
9108892ea20SAggelos Economopoulos 	size = sc->sram_size;
9118892ea20SAggelos Economopoulos 	status = mxge_load_firmware_helper(sc, &size);
9128892ea20SAggelos Economopoulos 	if (status) {
9138892ea20SAggelos Economopoulos 		if (!adopt)
9148892ea20SAggelos Economopoulos 			return status;
9157cc92483SSepherosa Ziehau 
9167cc92483SSepherosa Ziehau 		/*
9177cc92483SSepherosa Ziehau 		 * Try to use the currently running firmware, if
9187cc92483SSepherosa Ziehau 		 * it is new enough
9197cc92483SSepherosa Ziehau 		 */
9208892ea20SAggelos Economopoulos 		status = mxge_adopt_running_firmware(sc);
9218892ea20SAggelos Economopoulos 		if (status) {
9228a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9238892ea20SAggelos Economopoulos 			    "failed to adopt running firmware\n");
9248892ea20SAggelos Economopoulos 			return status;
9258892ea20SAggelos Economopoulos 		}
9268a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "Successfully adopted running firmware\n");
9277cc92483SSepherosa Ziehau 
9288892ea20SAggelos Economopoulos 		if (sc->tx_boundary == 4096) {
9298a20b038SSepherosa Ziehau 			if_printf(sc->ifp,
9307cc92483SSepherosa Ziehau 			     "Using firmware currently running on NIC.  "
9317cc92483SSepherosa Ziehau 			     "For optimal\n");
9328a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "performance consider loading "
9337cc92483SSepherosa Ziehau 			     "optimized firmware\n");
9348892ea20SAggelos Economopoulos 		}
9358892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_unaligned;
9368892ea20SAggelos Economopoulos 		sc->tx_boundary = 2048;
9378892ea20SAggelos Economopoulos 		return 0;
9388892ea20SAggelos Economopoulos 	}
9397cc92483SSepherosa Ziehau 
9407cc92483SSepherosa Ziehau 	/* Clear confirmation addr */
9418892ea20SAggelos Economopoulos 	confirm = (volatile uint32_t *)sc->cmd;
9428892ea20SAggelos Economopoulos 	*confirm = 0;
9438892ea20SAggelos Economopoulos 	wmb();
9447cc92483SSepherosa Ziehau 
9457cc92483SSepherosa Ziehau 	/*
9467cc92483SSepherosa Ziehau 	 * Send a reload command to the bootstrap MCP, and wait for the
9477cc92483SSepherosa Ziehau 	 * response in the confirmation address.  The firmware should
9487cc92483SSepherosa Ziehau 	 * write a -1 there to indicate it is alive and well
9498892ea20SAggelos Economopoulos 	 */
9508892ea20SAggelos Economopoulos 
9517cc92483SSepherosa Ziehau 	dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9527cc92483SSepherosa Ziehau 	dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.dmem_busaddr);
9538892ea20SAggelos Economopoulos 
9548892ea20SAggelos Economopoulos 	buf[0] = htobe32(dma_high);	/* confirm addr MSW */
9558892ea20SAggelos Economopoulos 	buf[1] = htobe32(dma_low);	/* confirm addr LSW */
9568892ea20SAggelos Economopoulos 	buf[2] = htobe32(0xffffffff);	/* confirm data */
9578892ea20SAggelos Economopoulos 
9587cc92483SSepherosa Ziehau 	/*
9597cc92483SSepherosa Ziehau 	 * FIX: All newest firmware should un-protect the bottom of
9607cc92483SSepherosa Ziehau 	 * the sram before handoff. However, the very first interfaces
9617cc92483SSepherosa Ziehau 	 * do not. Therefore the handoff copy must skip the first 8 bytes
9628892ea20SAggelos Economopoulos 	 */
9638892ea20SAggelos Economopoulos 					/* where the code starts*/
9648892ea20SAggelos Economopoulos 	buf[3] = htobe32(MXGE_FW_OFFSET + 8);
9658892ea20SAggelos Economopoulos 	buf[4] = htobe32(size - 8); 	/* length of code */
9668892ea20SAggelos Economopoulos 	buf[5] = htobe32(8);		/* where to copy to */
9678892ea20SAggelos Economopoulos 	buf[6] = htobe32(0);		/* where to jump to */
9688892ea20SAggelos Economopoulos 
9698892ea20SAggelos Economopoulos 	submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF);
9708892ea20SAggelos Economopoulos 	mxge_pio_copy(submit, buf, 64);
9718892ea20SAggelos Economopoulos 	wmb();
9728892ea20SAggelos Economopoulos 	DELAY(1000);
9738892ea20SAggelos Economopoulos 	wmb();
9748892ea20SAggelos Economopoulos 	i = 0;
9758892ea20SAggelos Economopoulos 	while (*confirm != 0xffffffff && i < 20) {
9768892ea20SAggelos Economopoulos 		DELAY(1000*10);
9778892ea20SAggelos Economopoulos 		i++;
9788892ea20SAggelos Economopoulos 	}
9798892ea20SAggelos Economopoulos 	if (*confirm != 0xffffffff) {
9808a20b038SSepherosa Ziehau 		if_printf(sc->ifp,"handoff failed (%p = 0x%x)",
9818892ea20SAggelos Economopoulos 		    confirm, *confirm);
9828892ea20SAggelos Economopoulos 		return ENXIO;
9838892ea20SAggelos Economopoulos 	}
9848892ea20SAggelos Economopoulos 	return 0;
9858892ea20SAggelos Economopoulos }
9868892ea20SAggelos Economopoulos 
9878892ea20SAggelos Economopoulos static int
mxge_update_mac_address(mxge_softc_t * sc)9888892ea20SAggelos Economopoulos mxge_update_mac_address(mxge_softc_t *sc)
9898892ea20SAggelos Economopoulos {
9908892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
9918892ea20SAggelos Economopoulos 	uint8_t *addr = sc->mac_addr;
9928892ea20SAggelos Economopoulos 
9937cc92483SSepherosa Ziehau 	cmd.data0 = (addr[0] << 24) | (addr[1] << 16) |
9947cc92483SSepherosa Ziehau 	    (addr[2] << 8) | addr[3];
9957cc92483SSepherosa Ziehau 	cmd.data1 = (addr[4] << 8) | (addr[5]);
9967cc92483SSepherosa Ziehau 	return mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd);
9978892ea20SAggelos Economopoulos }
9988892ea20SAggelos Economopoulos 
9998892ea20SAggelos Economopoulos static int
mxge_change_pause(mxge_softc_t * sc,int pause)10008892ea20SAggelos Economopoulos mxge_change_pause(mxge_softc_t *sc, int pause)
10018892ea20SAggelos Economopoulos {
10028892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10038892ea20SAggelos Economopoulos 	int status;
10048892ea20SAggelos Economopoulos 
100557e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
10068892ea20SAggelos Economopoulos 	if (pause)
10077cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, &cmd);
10088892ea20SAggelos Economopoulos 	else
10097cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, &cmd);
10108892ea20SAggelos Economopoulos 	if (status) {
10115a637e78SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set flow control mode\n");
10128892ea20SAggelos Economopoulos 		return ENXIO;
10138892ea20SAggelos Economopoulos 	}
10148892ea20SAggelos Economopoulos 	sc->pause = pause;
10158892ea20SAggelos Economopoulos 	return 0;
10168892ea20SAggelos Economopoulos }
10178892ea20SAggelos Economopoulos 
10188892ea20SAggelos Economopoulos static void
mxge_change_promisc(mxge_softc_t * sc,int promisc)10198892ea20SAggelos Economopoulos mxge_change_promisc(mxge_softc_t *sc, int promisc)
10208892ea20SAggelos Economopoulos {
10218892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10228892ea20SAggelos Economopoulos 	int status;
10238892ea20SAggelos Economopoulos 
102457e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* avoid gcc warning */
10258892ea20SAggelos Economopoulos 	if (mxge_always_promisc)
10268892ea20SAggelos Economopoulos 		promisc = 1;
10278892ea20SAggelos Economopoulos 
10288892ea20SAggelos Economopoulos 	if (promisc)
10297cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, &cmd);
10308892ea20SAggelos Economopoulos 	else
10317cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, &cmd);
10327cc92483SSepherosa Ziehau 	if (status)
1033af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Failed to set promisc mode\n");
10348892ea20SAggelos Economopoulos }
10358892ea20SAggelos Economopoulos 
10368892ea20SAggelos Economopoulos static void
mxge_set_multicast_list(mxge_softc_t * sc)10378892ea20SAggelos Economopoulos mxge_set_multicast_list(mxge_softc_t *sc)
10388892ea20SAggelos Economopoulos {
10398892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
10408892ea20SAggelos Economopoulos 	struct ifmultiaddr *ifma;
10418892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
10428892ea20SAggelos Economopoulos 	int err;
10438892ea20SAggelos Economopoulos 
10448892ea20SAggelos Economopoulos 	/* This firmware is known to not support multicast */
10458892ea20SAggelos Economopoulos 	if (!sc->fw_multicast_support)
10468892ea20SAggelos Economopoulos 		return;
10478892ea20SAggelos Economopoulos 
10488892ea20SAggelos Economopoulos 	/* Disable multicast filtering while we play with the lists*/
104957e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
10508892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd);
10518892ea20SAggelos Economopoulos 	if (err != 0) {
1052af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_ENABLE_ALLMULTI, "
10538892ea20SAggelos Economopoulos 		    "error status: %d\n", err);
10548892ea20SAggelos Economopoulos 		return;
10558892ea20SAggelos Economopoulos 	}
10568892ea20SAggelos Economopoulos 
10578892ea20SAggelos Economopoulos 	if (sc->adopted_rx_filter_bug)
10588892ea20SAggelos Economopoulos 		return;
10598892ea20SAggelos Economopoulos 
10607cc92483SSepherosa Ziehau 	if (ifp->if_flags & IFF_ALLMULTI) {
10617cc92483SSepherosa Ziehau 		/* Request to disable multicast filtering, so quit here */
10628892ea20SAggelos Economopoulos 		return;
10638892ea20SAggelos Economopoulos 	}
10648892ea20SAggelos Economopoulos 
10657cc92483SSepherosa Ziehau 	/* Flush all the filters */
10667cc92483SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd);
10677cc92483SSepherosa Ziehau 	if (err != 0) {
1068af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, "
10697cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
10707cc92483SSepherosa Ziehau 		return;
10717cc92483SSepherosa Ziehau 	}
10728892ea20SAggelos Economopoulos 
10737cc92483SSepherosa Ziehau 	/*
10747cc92483SSepherosa Ziehau 	 * Walk the multicast list, and add each address
10757cc92483SSepherosa Ziehau 	 */
1076441d34b2SSascha Wildner 	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
10778892ea20SAggelos Economopoulos 		if (ifma->ifma_addr->sa_family != AF_LINK)
10788892ea20SAggelos Economopoulos 			continue;
10797cc92483SSepherosa Ziehau 
10808892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
10818892ea20SAggelos Economopoulos 		    &cmd.data0, 4);
10828892ea20SAggelos Economopoulos 		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4,
10838892ea20SAggelos Economopoulos 		    &cmd.data1, 2);
10848892ea20SAggelos Economopoulos 		cmd.data0 = htonl(cmd.data0);
10858892ea20SAggelos Economopoulos 		cmd.data1 = htonl(cmd.data1);
10868892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd);
10878892ea20SAggelos Economopoulos 		if (err != 0) {
1088af85d4d5SSepherosa Ziehau 			if_printf(ifp, "Failed MXGEFW_JOIN_MULTICAST_GROUP, "
10897cc92483SSepherosa Ziehau 			    "error status: %d\n", err);
10907cc92483SSepherosa Ziehau 			/* Abort, leaving multicast filtering off */
10918892ea20SAggelos Economopoulos 			return;
10928892ea20SAggelos Economopoulos 		}
10938892ea20SAggelos Economopoulos 	}
10947cc92483SSepherosa Ziehau 
10958892ea20SAggelos Economopoulos 	/* Enable multicast filtering */
10968892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd);
10978892ea20SAggelos Economopoulos 	if (err != 0) {
1098af85d4d5SSepherosa Ziehau 		if_printf(ifp, "Failed MXGEFW_DISABLE_ALLMULTI, "
10997cc92483SSepherosa Ziehau 		    "error status: %d\n", err);
11008892ea20SAggelos Economopoulos 	}
11018892ea20SAggelos Economopoulos }
11028892ea20SAggelos Economopoulos 
110389d55360SSepherosa Ziehau #if 0
11048892ea20SAggelos Economopoulos static int
11058892ea20SAggelos Economopoulos mxge_max_mtu(mxge_softc_t *sc)
11068892ea20SAggelos Economopoulos {
11078892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
11088892ea20SAggelos Economopoulos 	int status;
11098892ea20SAggelos Economopoulos 
11108892ea20SAggelos Economopoulos 	if (MJUMPAGESIZE - MXGEFW_PAD >  MXGEFW_MAX_MTU)
11118892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11128892ea20SAggelos Economopoulos 
11138892ea20SAggelos Economopoulos 	/* try to set nbufs to see if it we can
11148892ea20SAggelos Economopoulos 	   use virtually contiguous jumbos */
11158892ea20SAggelos Economopoulos 	cmd.data0 = 0;
11168892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
11178892ea20SAggelos Economopoulos 			       &cmd);
11188892ea20SAggelos Economopoulos 	if (status == 0)
11198892ea20SAggelos Economopoulos 		return  MXGEFW_MAX_MTU - MXGEFW_PAD;
11208892ea20SAggelos Economopoulos 
11218892ea20SAggelos Economopoulos 	/* otherwise, we're limited to MJUMPAGESIZE */
11228892ea20SAggelos Economopoulos 	return MJUMPAGESIZE - MXGEFW_PAD;
11238892ea20SAggelos Economopoulos }
112489d55360SSepherosa Ziehau #endif
11258892ea20SAggelos Economopoulos 
11268892ea20SAggelos Economopoulos static int
mxge_reset(mxge_softc_t * sc,int interrupts_setup)11278892ea20SAggelos Economopoulos mxge_reset(mxge_softc_t *sc, int interrupts_setup)
11288892ea20SAggelos Economopoulos {
11298892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
11308892ea20SAggelos Economopoulos 	mxge_rx_done_t *rx_done;
11318892ea20SAggelos Economopoulos 	volatile uint32_t *irq_claim;
11328892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
1133089301c2SSepherosa Ziehau 	int slice, status, rx_intr_size;
11348892ea20SAggelos Economopoulos 
11357cc92483SSepherosa Ziehau 	/*
11367cc92483SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it
11377cc92483SSepherosa Ziehau 	 * is alive
11387cc92483SSepherosa Ziehau 	 */
11398892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof (cmd));
11408892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
11418892ea20SAggelos Economopoulos 	if (status != 0) {
11426ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed reset\n");
11438892ea20SAggelos Economopoulos 		return ENXIO;
11448892ea20SAggelos Economopoulos 	}
11458892ea20SAggelos Economopoulos 
11468892ea20SAggelos Economopoulos 	mxge_dummy_rdma(sc, 1);
11478892ea20SAggelos Economopoulos 
1148089301c2SSepherosa Ziehau 	/*
1149089301c2SSepherosa Ziehau 	 * Set the intrq size
1150089301c2SSepherosa Ziehau 	 * XXX assume 4byte mcp_slot
1151089301c2SSepherosa Ziehau 	 */
1152089301c2SSepherosa Ziehau 	rx_intr_size = sc->rx_intr_slots * sizeof(mcp_slot_t);
1153089301c2SSepherosa Ziehau 	cmd.data0 = rx_intr_size;
11548892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
11558892ea20SAggelos Economopoulos 
11568892ea20SAggelos Economopoulos 	/*
11578892ea20SAggelos Economopoulos 	 * Even though we already know how many slices are supported
11588892ea20SAggelos Economopoulos 	 * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES
11598892ea20SAggelos Economopoulos 	 * has magic side effects, and must be called after a reset.
11608892ea20SAggelos Economopoulos 	 * It must be called prior to calling any RSS related cmds,
11618892ea20SAggelos Economopoulos 	 * including assigning an interrupt queue for anything but
11628892ea20SAggelos Economopoulos 	 * slice 0.  It must also be called *after*
11638892ea20SAggelos Economopoulos 	 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
11648892ea20SAggelos Economopoulos 	 * the firmware to compute offsets.
11658892ea20SAggelos Economopoulos 	 */
11668892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
11677cc92483SSepherosa Ziehau 		/* Ask the maximum number of slices it supports */
11687cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
11698892ea20SAggelos Economopoulos 		if (status != 0) {
11706ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to get number of slices\n");
11718892ea20SAggelos Economopoulos 			return status;
11728892ea20SAggelos Economopoulos 		}
11737cc92483SSepherosa Ziehau 
11748892ea20SAggelos Economopoulos 		/*
11758892ea20SAggelos Economopoulos 		 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
11768892ea20SAggelos Economopoulos 		 * to setting up the interrupt queue DMA
11778892ea20SAggelos Economopoulos 		 */
11788892ea20SAggelos Economopoulos 		cmd.data0 = sc->num_slices;
11798892ea20SAggelos Economopoulos 		cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
1180aca8f373SSepherosa Ziehau 		if (sc->num_tx_rings > 1)
11818892ea20SAggelos Economopoulos 			cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
11827cc92483SSepherosa Ziehau 		status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, &cmd);
11838892ea20SAggelos Economopoulos 		if (status != 0) {
11846ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "failed to set number of slices\n");
11858892ea20SAggelos Economopoulos 			return status;
11868892ea20SAggelos Economopoulos 		}
11878892ea20SAggelos Economopoulos 	}
11888892ea20SAggelos Economopoulos 
11898892ea20SAggelos Economopoulos 	if (interrupts_setup) {
11908892ea20SAggelos Economopoulos 		/* Now exchange information about interrupts  */
11918892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
1192414caf0dSSepherosa Ziehau 			ss = &sc->ss[slice];
1193414caf0dSSepherosa Ziehau 
1194414caf0dSSepherosa Ziehau 			rx_done = &ss->rx_data.rx_done;
1195089301c2SSepherosa Ziehau 			memset(rx_done->entry, 0, rx_intr_size);
1196414caf0dSSepherosa Ziehau 
11977cc92483SSepherosa Ziehau 			cmd.data0 =
1198414caf0dSSepherosa Ziehau 			    MXGE_LOWPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
11997cc92483SSepherosa Ziehau 			cmd.data1 =
1200414caf0dSSepherosa Ziehau 			    MXGE_HIGHPART_TO_U32(ss->rx_done_dma.dmem_busaddr);
12018892ea20SAggelos Economopoulos 			cmd.data2 = slice;
12027cc92483SSepherosa Ziehau 			status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA,
12038892ea20SAggelos Economopoulos 			    &cmd);
12048892ea20SAggelos Economopoulos 		}
12058892ea20SAggelos Economopoulos 	}
12068892ea20SAggelos Economopoulos 
12077cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET,
12087cc92483SSepherosa Ziehau 	    &cmd);
12098892ea20SAggelos Economopoulos 	sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0);
12108892ea20SAggelos Economopoulos 
12118892ea20SAggelos Economopoulos 	status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd);
12128892ea20SAggelos Economopoulos 	irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0);
12138892ea20SAggelos Economopoulos 
12147cc92483SSepherosa Ziehau 	status |= mxge_send_cmd(sc,  MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
12158892ea20SAggelos Economopoulos 	sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0);
12167cc92483SSepherosa Ziehau 
12178892ea20SAggelos Economopoulos 	if (status != 0) {
12186ee6cba3SSepherosa Ziehau 		if_printf(sc->ifp, "failed set interrupt parameters\n");
12198892ea20SAggelos Economopoulos 		return status;
12208892ea20SAggelos Economopoulos 	}
12218892ea20SAggelos Economopoulos 
12228892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay);
12238892ea20SAggelos Economopoulos 
12247cc92483SSepherosa Ziehau 	/* Run a DMA benchmark */
12257cc92483SSepherosa Ziehau 	mxge_dma_test(sc, MXGEFW_DMA_TEST);
12268892ea20SAggelos Economopoulos 
12278892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
12288892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
12298892ea20SAggelos Economopoulos 
12308892ea20SAggelos Economopoulos 		ss->irq_claim = irq_claim + (2 * slice);
12317cc92483SSepherosa Ziehau 
12327cc92483SSepherosa Ziehau 		/* Reset mcp/driver shared state back to 0 */
12339a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.idx = 0;
12348892ea20SAggelos Economopoulos 		ss->tx.req = 0;
12358892ea20SAggelos Economopoulos 		ss->tx.done = 0;
12368892ea20SAggelos Economopoulos 		ss->tx.pkt_done = 0;
12378892ea20SAggelos Economopoulos 		ss->tx.queue_active = 0;
12388892ea20SAggelos Economopoulos 		ss->tx.activate = 0;
12398892ea20SAggelos Economopoulos 		ss->tx.deactivate = 0;
12409a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.cnt = 0;
12419a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.cnt = 0;
12427cc92483SSepherosa Ziehau 		if (ss->fw_stats != NULL)
12437cc92483SSepherosa Ziehau 			bzero(ss->fw_stats, sizeof(*ss->fw_stats));
12448892ea20SAggelos Economopoulos 	}
12458892ea20SAggelos Economopoulos 	sc->rdma_tags_available = 15;
12467cc92483SSepherosa Ziehau 
12478892ea20SAggelos Economopoulos 	status = mxge_update_mac_address(sc);
12488892ea20SAggelos Economopoulos 	mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC);
12498892ea20SAggelos Economopoulos 	mxge_change_pause(sc, sc->pause);
12508892ea20SAggelos Economopoulos 	mxge_set_multicast_list(sc);
12517cc92483SSepherosa Ziehau 
125289d55360SSepherosa Ziehau 	if (sc->throttle) {
125389d55360SSepherosa Ziehau 		cmd.data0 = sc->throttle;
12547cc92483SSepherosa Ziehau 		if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd))
12556ee6cba3SSepherosa Ziehau 			if_printf(sc->ifp, "can't enable throttle\n");
125689d55360SSepherosa Ziehau 	}
12578892ea20SAggelos Economopoulos 	return status;
12588892ea20SAggelos Economopoulos }
12598892ea20SAggelos Economopoulos 
12608892ea20SAggelos Economopoulos static int
mxge_change_throttle(SYSCTL_HANDLER_ARGS)126189d55360SSepherosa Ziehau mxge_change_throttle(SYSCTL_HANDLER_ARGS)
126289d55360SSepherosa Ziehau {
126389d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
126489d55360SSepherosa Ziehau 	mxge_softc_t *sc;
126589d55360SSepherosa Ziehau 	int err;
126689d55360SSepherosa Ziehau 	unsigned int throttle;
126789d55360SSepherosa Ziehau 
126889d55360SSepherosa Ziehau 	sc = arg1;
126989d55360SSepherosa Ziehau 	throttle = sc->throttle;
127089d55360SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &throttle, arg2, req);
12715a637e78SSepherosa Ziehau 	if (err != 0)
127289d55360SSepherosa Ziehau 		return err;
127389d55360SSepherosa Ziehau 
127489d55360SSepherosa Ziehau 	if (throttle == sc->throttle)
127589d55360SSepherosa Ziehau 		return 0;
127689d55360SSepherosa Ziehau 
127789d55360SSepherosa Ziehau 	if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE)
127889d55360SSepherosa Ziehau 		return EINVAL;
127989d55360SSepherosa Ziehau 
128026634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
128189d55360SSepherosa Ziehau 
128289d55360SSepherosa Ziehau 	cmd.data0 = throttle;
128389d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd);
128489d55360SSepherosa Ziehau 	if (err == 0)
128589d55360SSepherosa Ziehau 		sc->throttle = throttle;
128689d55360SSepherosa Ziehau 
128726634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
128889d55360SSepherosa Ziehau 	return err;
128989d55360SSepherosa Ziehau }
129089d55360SSepherosa Ziehau 
129189d55360SSepherosa Ziehau static int
mxge_change_use_rss(SYSCTL_HANDLER_ARGS)12928433e5f5SSepherosa Ziehau mxge_change_use_rss(SYSCTL_HANDLER_ARGS)
12938433e5f5SSepherosa Ziehau {
12948433e5f5SSepherosa Ziehau 	mxge_softc_t *sc;
12958433e5f5SSepherosa Ziehau 	int err, use_rss;
12968433e5f5SSepherosa Ziehau 
12978433e5f5SSepherosa Ziehau 	sc = arg1;
12988433e5f5SSepherosa Ziehau 	use_rss = sc->use_rss;
12998433e5f5SSepherosa Ziehau 	err = sysctl_handle_int(oidp, &use_rss, arg2, req);
13008433e5f5SSepherosa Ziehau 	if (err != 0)
13018433e5f5SSepherosa Ziehau 		return err;
13028433e5f5SSepherosa Ziehau 
13038433e5f5SSepherosa Ziehau 	if (use_rss == sc->use_rss)
13048433e5f5SSepherosa Ziehau 		return 0;
13058433e5f5SSepherosa Ziehau 
13068433e5f5SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
13078433e5f5SSepherosa Ziehau 
13088433e5f5SSepherosa Ziehau 	sc->use_rss = use_rss;
13098433e5f5SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
13108433e5f5SSepherosa Ziehau 		mxge_close(sc, 0);
13118433e5f5SSepherosa Ziehau 		mxge_open(sc);
13128433e5f5SSepherosa Ziehau 	}
13138433e5f5SSepherosa Ziehau 
13148433e5f5SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13158433e5f5SSepherosa Ziehau 	return err;
13168433e5f5SSepherosa Ziehau }
13178433e5f5SSepherosa Ziehau 
13188433e5f5SSepherosa Ziehau static int
mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)13198892ea20SAggelos Economopoulos mxge_change_intr_coal(SYSCTL_HANDLER_ARGS)
13208892ea20SAggelos Economopoulos {
13218892ea20SAggelos Economopoulos 	mxge_softc_t *sc;
13228892ea20SAggelos Economopoulos 	unsigned int intr_coal_delay;
13238892ea20SAggelos Economopoulos 	int err;
13248892ea20SAggelos Economopoulos 
13258892ea20SAggelos Economopoulos 	sc = arg1;
13268892ea20SAggelos Economopoulos 	intr_coal_delay = sc->intr_coal_delay;
13278892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req);
13285a637e78SSepherosa Ziehau 	if (err != 0)
13298892ea20SAggelos Economopoulos 		return err;
13305a637e78SSepherosa Ziehau 
13318892ea20SAggelos Economopoulos 	if (intr_coal_delay == sc->intr_coal_delay)
13328892ea20SAggelos Economopoulos 		return 0;
13338892ea20SAggelos Economopoulos 
13348892ea20SAggelos Economopoulos 	if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000)
13358892ea20SAggelos Economopoulos 		return EINVAL;
13368892ea20SAggelos Economopoulos 
133726634ef8SSepherosa Ziehau 	ifnet_serialize_all(sc->ifp);
133889d55360SSepherosa Ziehau 
13398892ea20SAggelos Economopoulos 	*sc->intr_coal_delay_ptr = htobe32(intr_coal_delay);
13408892ea20SAggelos Economopoulos 	sc->intr_coal_delay = intr_coal_delay;
13418892ea20SAggelos Economopoulos 
134226634ef8SSepherosa Ziehau 	ifnet_deserialize_all(sc->ifp);
13438892ea20SAggelos Economopoulos 	return err;
13448892ea20SAggelos Economopoulos }
13458892ea20SAggelos Economopoulos 
13468892ea20SAggelos Economopoulos static int
mxge_handle_be32(SYSCTL_HANDLER_ARGS)13478892ea20SAggelos Economopoulos mxge_handle_be32(SYSCTL_HANDLER_ARGS)
13488892ea20SAggelos Economopoulos {
13498892ea20SAggelos Economopoulos 	int err;
13508892ea20SAggelos Economopoulos 
13518892ea20SAggelos Economopoulos 	if (arg1 == NULL)
13528892ea20SAggelos Economopoulos 		return EFAULT;
13538892ea20SAggelos Economopoulos 	arg2 = be32toh(*(int *)arg1);
13548892ea20SAggelos Economopoulos 	arg1 = NULL;
13558892ea20SAggelos Economopoulos 	err = sysctl_handle_int(oidp, arg1, arg2, req);
13568892ea20SAggelos Economopoulos 
13578892ea20SAggelos Economopoulos 	return err;
13588892ea20SAggelos Economopoulos }
13598892ea20SAggelos Economopoulos 
13608892ea20SAggelos Economopoulos static void
mxge_rem_sysctls(mxge_softc_t * sc)13618892ea20SAggelos Economopoulos mxge_rem_sysctls(mxge_softc_t *sc)
13628892ea20SAggelos Economopoulos {
1363798c3369SSepherosa Ziehau 	if (sc->ss != NULL) {
13648892ea20SAggelos Economopoulos 		struct mxge_slice_state *ss;
13658892ea20SAggelos Economopoulos 		int slice;
13668892ea20SAggelos Economopoulos 
13678892ea20SAggelos Economopoulos 		for (slice = 0; slice < sc->num_slices; slice++) {
13688892ea20SAggelos Economopoulos 			ss = &sc->ss[slice];
1369798c3369SSepherosa Ziehau 			if (ss->sysctl_tree != NULL) {
13708892ea20SAggelos Economopoulos 				sysctl_ctx_free(&ss->sysctl_ctx);
13718892ea20SAggelos Economopoulos 				ss->sysctl_tree = NULL;
13728892ea20SAggelos Economopoulos 			}
1373798c3369SSepherosa Ziehau 		}
1374798c3369SSepherosa Ziehau 	}
1375798c3369SSepherosa Ziehau 
1376798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree != NULL) {
13778892ea20SAggelos Economopoulos 		sysctl_ctx_free(&sc->slice_sysctl_ctx);
13788892ea20SAggelos Economopoulos 		sc->slice_sysctl_tree = NULL;
1379798c3369SSepherosa Ziehau 	}
1380798c3369SSepherosa Ziehau }
13818892ea20SAggelos Economopoulos 
13828892ea20SAggelos Economopoulos static void
mxge_add_sysctls(mxge_softc_t * sc)13838892ea20SAggelos Economopoulos mxge_add_sysctls(mxge_softc_t *sc)
13848892ea20SAggelos Economopoulos {
13858892ea20SAggelos Economopoulos 	struct sysctl_ctx_list *ctx;
13868892ea20SAggelos Economopoulos 	struct sysctl_oid_list *children;
13878892ea20SAggelos Economopoulos 	mcp_irq_data_t *fw;
13888892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
13898892ea20SAggelos Economopoulos 	int slice;
13908892ea20SAggelos Economopoulos 	char slice_num[8];
13918892ea20SAggelos Economopoulos 
139226595b18SSascha Wildner 	ctx = device_get_sysctl_ctx(sc->dev);
139326595b18SSascha Wildner 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
13948892ea20SAggelos Economopoulos 	fw = sc->ss[0].fw_stats;
13958892ea20SAggelos Economopoulos 
13967cc92483SSepherosa Ziehau 	/*
13977cc92483SSepherosa Ziehau 	 * Random information
13987cc92483SSepherosa Ziehau 	 */
13997cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version",
14007cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->fw_version, 0, "firmware version");
14018892ea20SAggelos Economopoulos 
14027cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial_number",
14037cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->serial_number_string, 0, "serial number");
14048892ea20SAggelos Economopoulos 
14057cc92483SSepherosa Ziehau 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "product_code",
14067cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->product_code_string, 0, "product code");
14078892ea20SAggelos Economopoulos 
14087cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "pcie_link_width",
14097cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->link_width, 0, "link width");
141089d55360SSepherosa Ziehau 
14117cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_boundary",
14127cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->tx_boundary, 0, "tx boundary");
14138892ea20SAggelos Economopoulos 
14147cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_combine",
14157cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->wc, 0, "write combining PIO");
14168892ea20SAggelos Economopoulos 
14177cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_dma_MBs",
14187cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_dma, 0, "DMA Read speed in MB/s");
14198892ea20SAggelos Economopoulos 
14207cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "write_dma_MBs",
14217cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->write_dma, 0, "DMA Write speed in MB/s");
14228892ea20SAggelos Economopoulos 
14237cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "read_write_dma_MBs",
14247cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->read_write_dma, 0,
14257cc92483SSepherosa Ziehau 	    "DMA concurrent Read/Write speed in MB/s");
14267cc92483SSepherosa Ziehau 
14277cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "watchdog_resets",
14287cc92483SSepherosa Ziehau 	    CTLFLAG_RD, &sc->watchdog_resets, 0,
14297cc92483SSepherosa Ziehau 	    "Number of times NIC was reset");
14307cc92483SSepherosa Ziehau 
1431bdbc20adSSepherosa Ziehau 	if (sc->num_slices > 1) {
1432bdbc20adSSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "slice_cpumap",
1433bdbc20adSSepherosa Ziehau 		    CTLTYPE_OPAQUE | CTLFLAG_RD, sc->ring_map, 0,
1434bdbc20adSSepherosa Ziehau 		    if_ringmap_cpumap_sysctl, "I", "slice CPU map");
1435bdbc20adSSepherosa Ziehau 	}
1436bdbc20adSSepherosa Ziehau 
14377cc92483SSepherosa Ziehau 	/*
14387cc92483SSepherosa Ziehau 	 * Performance related tunables
14397cc92483SSepherosa Ziehau 	 */
14407cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal_delay",
14417cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_intr_coal, "I",
14427cc92483SSepherosa Ziehau 	    "Interrupt coalescing delay in usecs");
14437cc92483SSepherosa Ziehau 
14447cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "throttle",
14457cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_throttle, "I",
14467cc92483SSepherosa Ziehau 	    "Transmit throttling");
14477cc92483SSepherosa Ziehau 
14488433e5f5SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "use_rss",
14498433e5f5SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RW, sc, 0, mxge_change_use_rss, "I",
14508433e5f5SSepherosa Ziehau 	    "Use RSS");
14518433e5f5SSepherosa Ziehau 
14527cc92483SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "deassert_wait",
14537cc92483SSepherosa Ziehau 	    CTLFLAG_RW, &mxge_deassert_wait, 0,
14547cc92483SSepherosa Ziehau 	    "Wait for IRQ line to go low in ihandler");
14557cc92483SSepherosa Ziehau 
14567cc92483SSepherosa Ziehau 	/*
14577cc92483SSepherosa Ziehau 	 * Stats block from firmware is in network byte order.
14587cc92483SSepherosa Ziehau 	 * Need to swap it
14597cc92483SSepherosa Ziehau 	 */
14607cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "link_up",
14617cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 0,
14627cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "link up");
14637cc92483SSepherosa Ziehau 
14647cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rdma_tags_available",
14657cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 0,
14667cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "rdma_tags_available");
14677cc92483SSepherosa Ziehau 
14687cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_crc32",
14697cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_crc32, 0,
14707cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_crc32");
14717cc92483SSepherosa Ziehau 
14727cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_bad_phy",
14737cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_bad_phy, 0,
14747cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_bad_phy");
14757cc92483SSepherosa Ziehau 
14767cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_error_or_filtered",
14777cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_error_or_filtered, 0,
14787cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_error_or_filtered");
14797cc92483SSepherosa Ziehau 
14807cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_link_overflow",
14817cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 0,
14827cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_link_overflow");
14837cc92483SSepherosa Ziehau 
14847cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_multicast_filtered",
14857cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_multicast_filtered, 0,
14867cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_multicast_filtered");
14877cc92483SSepherosa Ziehau 
14887cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_big_buffer",
14897cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 0,
14907cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_big_buffer");
14917cc92483SSepherosa Ziehau 
14927cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_no_small_buffer",
14937cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_small_buffer, 0,
14947cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_no_small_buffer");
14957cc92483SSepherosa Ziehau 
14967cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_overrun",
14977cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 0,
14987cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_overrun");
14997cc92483SSepherosa Ziehau 
15007cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_pause",
15017cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_pause, 0,
15027cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_pause");
15037cc92483SSepherosa Ziehau 
15047cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_runt",
15057cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 0,
15067cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_runt");
15077cc92483SSepherosa Ziehau 
15087cc92483SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dropped_unicast_filtered",
15097cc92483SSepherosa Ziehau 	    CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 0,
15107cc92483SSepherosa Ziehau 	    mxge_handle_be32, "I", "dropped_unicast_filtered");
15118892ea20SAggelos Economopoulos 
15128892ea20SAggelos Economopoulos 	/* add counters exported for debugging from all slices */
15138892ea20SAggelos Economopoulos 	sysctl_ctx_init(&sc->slice_sysctl_ctx);
15147cc92483SSepherosa Ziehau 	sc->slice_sysctl_tree = SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx,
15157cc92483SSepherosa Ziehau 	    children, OID_AUTO, "slice", CTLFLAG_RD, 0, "");
1516798c3369SSepherosa Ziehau 	if (sc->slice_sysctl_tree == NULL) {
1517798c3369SSepherosa Ziehau 		device_printf(sc->dev, "can't add slice sysctl node\n");
1518798c3369SSepherosa Ziehau 		return;
1519798c3369SSepherosa Ziehau 	}
15208892ea20SAggelos Economopoulos 
15218892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
15228892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
15238892ea20SAggelos Economopoulos 		sysctl_ctx_init(&ss->sysctl_ctx);
15248892ea20SAggelos Economopoulos 		ctx = &ss->sysctl_ctx;
15258892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(sc->slice_sysctl_tree);
1526b6737651SAggelos Economopoulos 		ksprintf(slice_num, "%d", slice);
15277cc92483SSepherosa Ziehau 		ss->sysctl_tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO,
15287cc92483SSepherosa Ziehau 		    slice_num, CTLFLAG_RD, 0, "");
1529798c3369SSepherosa Ziehau 		if (ss->sysctl_tree == NULL) {
1530798c3369SSepherosa Ziehau 			device_printf(sc->dev,
1531798c3369SSepherosa Ziehau 			    "can't add %d slice sysctl node\n", slice);
1532798c3369SSepherosa Ziehau 			return;	/* XXX continue? */
1533798c3369SSepherosa Ziehau 		}
15348892ea20SAggelos Economopoulos 		children = SYSCTL_CHILDREN(ss->sysctl_tree);
15357cc92483SSepherosa Ziehau 
15367cc92483SSepherosa Ziehau 		/*
15377cc92483SSepherosa Ziehau 		 * XXX change to ULONG
15387cc92483SSepherosa Ziehau 		 */
15397cc92483SSepherosa Ziehau 
15407cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_small_cnt",
15419a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_small.cnt, 0, "rx_small_cnt");
15427cc92483SSepherosa Ziehau 
15437cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_big_cnt",
15449a4ae890SSepherosa Ziehau 		    CTLFLAG_RD, &ss->rx_data.rx_big.cnt, 0, "rx_small_cnt");
15458892ea20SAggelos Economopoulos 
15467cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_req",
15477cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.req, 0, "tx_req");
15487cc92483SSepherosa Ziehau 
15497cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_done",
15507cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.done, 0, "tx_done");
15517cc92483SSepherosa Ziehau 
15527cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_pkt_done",
15537cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.pkt_done, 0, "tx_done");
15547cc92483SSepherosa Ziehau 
15557cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_queue_active",
15567cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.queue_active, 0, "tx_queue_active");
15577cc92483SSepherosa Ziehau 
15587cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_activate",
15597cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.activate, 0, "tx_activate");
15607cc92483SSepherosa Ziehau 
15617cc92483SSepherosa Ziehau 		SYSCTL_ADD_INT(ctx, children, OID_AUTO, "tx_deactivate",
15627cc92483SSepherosa Ziehau 		    CTLFLAG_RD, &ss->tx.deactivate, 0, "tx_deactivate");
15638892ea20SAggelos Economopoulos 	}
15648892ea20SAggelos Economopoulos }
15658892ea20SAggelos Economopoulos 
156689d55360SSepherosa Ziehau /*
156789d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
156889d55360SSepherosa Ziehau  * backwards one at a time and handle ring wraps
156989d55360SSepherosa Ziehau  */
1570ddbf91b7SSepherosa Ziehau static __inline void
mxge_submit_req_backwards(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)15718892ea20SAggelos Economopoulos mxge_submit_req_backwards(mxge_tx_ring_t *tx,
15728892ea20SAggelos Economopoulos     mcp_kreq_ether_send_t *src, int cnt)
15738892ea20SAggelos Economopoulos {
15748892ea20SAggelos Economopoulos 	int idx, starting_slot;
15755ca32f31SSepherosa Ziehau 
15768892ea20SAggelos Economopoulos 	starting_slot = tx->req;
15778892ea20SAggelos Economopoulos 	while (cnt > 1) {
15788892ea20SAggelos Economopoulos 		cnt--;
15798892ea20SAggelos Economopoulos 		idx = (starting_slot + cnt) & tx->mask;
15805ca32f31SSepherosa Ziehau 		mxge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
15818892ea20SAggelos Economopoulos 		wmb();
15828892ea20SAggelos Economopoulos 	}
15838892ea20SAggelos Economopoulos }
15848892ea20SAggelos Economopoulos 
15858892ea20SAggelos Economopoulos /*
158689d55360SSepherosa Ziehau  * Copy an array of mcp_kreq_ether_send_t's to the mcp.  Copy
15878892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
15888892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's flags
15898892ea20SAggelos Economopoulos  * to mark them valid only after writing the entire chain
15908892ea20SAggelos Economopoulos  */
1591ddbf91b7SSepherosa Ziehau static __inline void
mxge_submit_req(mxge_tx_ring_t * tx,mcp_kreq_ether_send_t * src,int cnt)159289d55360SSepherosa Ziehau mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, int cnt)
15938892ea20SAggelos Economopoulos {
15948892ea20SAggelos Economopoulos 	int idx, i;
15958892ea20SAggelos Economopoulos 	uint32_t *src_ints;
15968892ea20SAggelos Economopoulos 	volatile uint32_t *dst_ints;
15978892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *srcp;
15988892ea20SAggelos Economopoulos 	volatile mcp_kreq_ether_send_t *dstp, *dst;
15998892ea20SAggelos Economopoulos 	uint8_t last_flags;
16008892ea20SAggelos Economopoulos 
16018892ea20SAggelos Economopoulos 	idx = tx->req & tx->mask;
16028892ea20SAggelos Economopoulos 
16038892ea20SAggelos Economopoulos 	last_flags = src->flags;
16048892ea20SAggelos Economopoulos 	src->flags = 0;
16058892ea20SAggelos Economopoulos 	wmb();
16068892ea20SAggelos Economopoulos 	dst = dstp = &tx->lanai[idx];
16078892ea20SAggelos Economopoulos 	srcp = src;
16088892ea20SAggelos Economopoulos 
16098892ea20SAggelos Economopoulos 	if ((idx + cnt) < tx->mask) {
16105ca32f31SSepherosa Ziehau 		for (i = 0; i < cnt - 1; i += 2) {
16118892ea20SAggelos Economopoulos 			mxge_pio_copy(dstp, srcp, 2 * sizeof(*src));
16128892ea20SAggelos Economopoulos 			wmb(); /* force write every 32 bytes */
16138892ea20SAggelos Economopoulos 			srcp += 2;
16148892ea20SAggelos Economopoulos 			dstp += 2;
16158892ea20SAggelos Economopoulos 		}
16168892ea20SAggelos Economopoulos 	} else {
16175ca32f31SSepherosa Ziehau 		/*
16185ca32f31SSepherosa Ziehau 		 * Submit all but the first request, and ensure
16195ca32f31SSepherosa Ziehau 		 * that it is submitted below
16205ca32f31SSepherosa Ziehau 		 */
16218892ea20SAggelos Economopoulos 		mxge_submit_req_backwards(tx, src, cnt);
16228892ea20SAggelos Economopoulos 		i = 0;
16238892ea20SAggelos Economopoulos 	}
16248892ea20SAggelos Economopoulos 	if (i < cnt) {
16255ca32f31SSepherosa Ziehau 		/* Submit the first request */
16268892ea20SAggelos Economopoulos 		mxge_pio_copy(dstp, srcp, sizeof(*src));
16278892ea20SAggelos Economopoulos 		wmb(); /* barrier before setting valid flag */
16288892ea20SAggelos Economopoulos 	}
16298892ea20SAggelos Economopoulos 
16305ca32f31SSepherosa Ziehau 	/* Re-write the last 32-bits with the valid flags */
16318892ea20SAggelos Economopoulos 	src->flags = last_flags;
16328892ea20SAggelos Economopoulos 	src_ints = (uint32_t *)src;
16338892ea20SAggelos Economopoulos 	src_ints+=3;
16348892ea20SAggelos Economopoulos 	dst_ints = (volatile uint32_t *)dst;
16358892ea20SAggelos Economopoulos 	dst_ints+=3;
16368892ea20SAggelos Economopoulos 	*dst_ints = *src_ints;
16378892ea20SAggelos Economopoulos 	tx->req += cnt;
16388892ea20SAggelos Economopoulos 	wmb();
16398892ea20SAggelos Economopoulos }
16408892ea20SAggelos Economopoulos 
164189d55360SSepherosa Ziehau static int
mxge_pullup_tso(struct mbuf ** mp)164289d55360SSepherosa Ziehau mxge_pullup_tso(struct mbuf **mp)
164389d55360SSepherosa Ziehau {
164489d55360SSepherosa Ziehau 	int hoff, iphlen, thoff;
164589d55360SSepherosa Ziehau 	struct mbuf *m;
164689d55360SSepherosa Ziehau 
164789d55360SSepherosa Ziehau 	m = *mp;
164889d55360SSepherosa Ziehau 	KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
164989d55360SSepherosa Ziehau 
165089d55360SSepherosa Ziehau 	iphlen = m->m_pkthdr.csum_iphlen;
165189d55360SSepherosa Ziehau 	thoff = m->m_pkthdr.csum_thlen;
165289d55360SSepherosa Ziehau 	hoff = m->m_pkthdr.csum_lhlen;
165389d55360SSepherosa Ziehau 
165489d55360SSepherosa Ziehau 	KASSERT(iphlen > 0, ("invalid ip hlen"));
165589d55360SSepherosa Ziehau 	KASSERT(thoff > 0, ("invalid tcp hlen"));
165689d55360SSepherosa Ziehau 	KASSERT(hoff > 0, ("invalid ether hlen"));
165789d55360SSepherosa Ziehau 
165889d55360SSepherosa Ziehau 	if (__predict_false(m->m_len < hoff + iphlen + thoff)) {
165989d55360SSepherosa Ziehau 		m = m_pullup(m, hoff + iphlen + thoff);
166089d55360SSepherosa Ziehau 		if (m == NULL) {
166189d55360SSepherosa Ziehau 			*mp = NULL;
166289d55360SSepherosa Ziehau 			return ENOBUFS;
166389d55360SSepherosa Ziehau 		}
166489d55360SSepherosa Ziehau 		*mp = m;
166589d55360SSepherosa Ziehau 	}
166689d55360SSepherosa Ziehau 	return 0;
166789d55360SSepherosa Ziehau }
16688892ea20SAggelos Economopoulos 
1669ca8ca004SSepherosa Ziehau static int
mxge_encap_tso(mxge_tx_ring_t * tx,struct mxge_buffer_state * info_map,struct mbuf * m,int busdma_seg_cnt)1670fb39a573SSepherosa Ziehau mxge_encap_tso(mxge_tx_ring_t *tx, struct mxge_buffer_state *info_map,
167148d12a0bSSepherosa Ziehau     struct mbuf *m, int busdma_seg_cnt)
16728892ea20SAggelos Economopoulos {
16738892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
16748892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
16758892ea20SAggelos Economopoulos 	uint32_t low, high_swapped;
16768892ea20SAggelos Economopoulos 	int len, seglen, cum_len, cum_len_next;
16778892ea20SAggelos Economopoulos 	int next_is_first, chop, cnt, rdma_count, small;
16788892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset, cksum_offset, mss;
16798892ea20SAggelos Economopoulos 	uint8_t flags, flags_next;
1680fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_last;
168148d12a0bSSepherosa Ziehau 	bus_dmamap_t map = info_map->map;
16828892ea20SAggelos Economopoulos 
16838892ea20SAggelos Economopoulos 	mss = m->m_pkthdr.tso_segsz;
16848892ea20SAggelos Economopoulos 
16855ca32f31SSepherosa Ziehau 	/*
16865ca32f31SSepherosa Ziehau 	 * Negative cum_len signifies to the send loop that we are
16875ca32f31SSepherosa Ziehau 	 * still in the header portion of the TSO packet.
16888892ea20SAggelos Economopoulos 	 */
168989d55360SSepherosa Ziehau 	cum_len = -(m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen +
169089d55360SSepherosa Ziehau 	    m->m_pkthdr.csum_thlen);
16918892ea20SAggelos Economopoulos 
16925ca32f31SSepherosa Ziehau 	/*
16935ca32f31SSepherosa Ziehau 	 * TSO implies checksum offload on this hardware
16945ca32f31SSepherosa Ziehau 	 */
169589d55360SSepherosa Ziehau 	cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
16968892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST;
16978892ea20SAggelos Economopoulos 
16985ca32f31SSepherosa Ziehau 	/*
16995ca32f31SSepherosa Ziehau 	 * For TSO, pseudo_hdr_offset holds mss.  The firmware figures
17005ca32f31SSepherosa Ziehau 	 * out where to put the checksum by parsing the header.
17015ca32f31SSepherosa Ziehau 	 */
17028892ea20SAggelos Economopoulos 	pseudo_hdr_offset = htobe16(mss);
17038892ea20SAggelos Economopoulos 
17048892ea20SAggelos Economopoulos 	req = tx->req_list;
17058892ea20SAggelos Economopoulos 	seg = tx->seg_list;
17068892ea20SAggelos Economopoulos 	cnt = 0;
17078892ea20SAggelos Economopoulos 	rdma_count = 0;
17085ca32f31SSepherosa Ziehau 
17095ca32f31SSepherosa Ziehau 	/*
17105ca32f31SSepherosa Ziehau 	 * "rdma_count" is the number of RDMAs belonging to the current
17115ca32f31SSepherosa Ziehau 	 * packet BEFORE the current send request.  For non-TSO packets,
17125ca32f31SSepherosa Ziehau 	 * this is equal to "count".
17138892ea20SAggelos Economopoulos 	 *
17145ca32f31SSepherosa Ziehau 	 * For TSO packets, rdma_count needs to be reset to 0 after a
17155ca32f31SSepherosa Ziehau 	 * segment cut.
17168892ea20SAggelos Economopoulos 	 *
17175ca32f31SSepherosa Ziehau 	 * The rdma_count field of the send request is the number of
17185ca32f31SSepherosa Ziehau 	 * RDMAs of the packet starting at that request.  For TSO send
17195ca32f31SSepherosa Ziehau 	 * requests with one ore more cuts in the middle, this is the
17205ca32f31SSepherosa Ziehau 	 * number of RDMAs starting after the last cut in the request.
17215ca32f31SSepherosa Ziehau 	 * All previous segments before the last cut implicitly have 1
17225ca32f31SSepherosa Ziehau 	 * RDMA.
17235ca32f31SSepherosa Ziehau 	 *
17245ca32f31SSepherosa Ziehau 	 * Since the number of RDMAs is not known beforehand, it must be
17255ca32f31SSepherosa Ziehau 	 * filled-in retroactively - after each segmentation cut or at
17265ca32f31SSepherosa Ziehau 	 * the end of the entire packet.
17278892ea20SAggelos Economopoulos 	 */
17288892ea20SAggelos Economopoulos 
17298892ea20SAggelos Economopoulos 	while (busdma_seg_cnt) {
17305ca32f31SSepherosa Ziehau 		/*
17315ca32f31SSepherosa Ziehau 		 * Break the busdma segment up into pieces
17325ca32f31SSepherosa Ziehau 		 */
17338892ea20SAggelos Economopoulos 		low = MXGE_LOWPART_TO_U32(seg->ds_addr);
17348892ea20SAggelos Economopoulos 		high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
17358892ea20SAggelos Economopoulos 		len = seg->ds_len;
17368892ea20SAggelos Economopoulos 
17378892ea20SAggelos Economopoulos 		while (len) {
17388892ea20SAggelos Economopoulos 			flags_next = flags & ~MXGEFW_FLAGS_FIRST;
17398892ea20SAggelos Economopoulos 			seglen = len;
17408892ea20SAggelos Economopoulos 			cum_len_next = cum_len + seglen;
17418892ea20SAggelos Economopoulos 			(req - rdma_count)->rdma_count = rdma_count + 1;
17428892ea20SAggelos Economopoulos 			if (__predict_true(cum_len >= 0)) {
17435ca32f31SSepherosa Ziehau 				/* Payload */
17448892ea20SAggelos Economopoulos 				chop = (cum_len_next > mss);
17458892ea20SAggelos Economopoulos 				cum_len_next = cum_len_next % mss;
17468892ea20SAggelos Economopoulos 				next_is_first = (cum_len_next == 0);
17478892ea20SAggelos Economopoulos 				flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
17485ca32f31SSepherosa Ziehau 				flags_next |=
17495ca32f31SSepherosa Ziehau 				    next_is_first * MXGEFW_FLAGS_FIRST;
17508892ea20SAggelos Economopoulos 				rdma_count |= -(chop | next_is_first);
17518892ea20SAggelos Economopoulos 				rdma_count += chop & !next_is_first;
17528892ea20SAggelos Economopoulos 			} else if (cum_len_next >= 0) {
17535ca32f31SSepherosa Ziehau 				/* Header ends */
17548892ea20SAggelos Economopoulos 				rdma_count = -1;
17558892ea20SAggelos Economopoulos 				cum_len_next = 0;
17568892ea20SAggelos Economopoulos 				seglen = -cum_len;
17578892ea20SAggelos Economopoulos 				small = (mss <= MXGEFW_SEND_SMALL_SIZE);
17588892ea20SAggelos Economopoulos 				flags_next = MXGEFW_FLAGS_TSO_PLD |
17598892ea20SAggelos Economopoulos 				    MXGEFW_FLAGS_FIRST |
17608892ea20SAggelos Economopoulos 				    (small * MXGEFW_FLAGS_SMALL);
17618892ea20SAggelos Economopoulos 			}
17628892ea20SAggelos Economopoulos 
17638892ea20SAggelos Economopoulos 			req->addr_high = high_swapped;
17648892ea20SAggelos Economopoulos 			req->addr_low = htobe32(low);
17658892ea20SAggelos Economopoulos 			req->pseudo_hdr_offset = pseudo_hdr_offset;
17668892ea20SAggelos Economopoulos 			req->pad = 0;
17678892ea20SAggelos Economopoulos 			req->rdma_count = 1;
17688892ea20SAggelos Economopoulos 			req->length = htobe16(seglen);
17698892ea20SAggelos Economopoulos 			req->cksum_offset = cksum_offset;
17705ca32f31SSepherosa Ziehau 			req->flags =
17715ca32f31SSepherosa Ziehau 			    flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD);
17728892ea20SAggelos Economopoulos 			low += seglen;
17738892ea20SAggelos Economopoulos 			len -= seglen;
17748892ea20SAggelos Economopoulos 			cum_len = cum_len_next;
17758892ea20SAggelos Economopoulos 			flags = flags_next;
17768892ea20SAggelos Economopoulos 			req++;
17778892ea20SAggelos Economopoulos 			cnt++;
17788892ea20SAggelos Economopoulos 			rdma_count++;
17798892ea20SAggelos Economopoulos 			if (__predict_false(cksum_offset > seglen))
17808892ea20SAggelos Economopoulos 				cksum_offset -= seglen;
17818892ea20SAggelos Economopoulos 			else
17828892ea20SAggelos Economopoulos 				cksum_offset = 0;
17838892ea20SAggelos Economopoulos 			if (__predict_false(cnt > tx->max_desc))
17848892ea20SAggelos Economopoulos 				goto drop;
17858892ea20SAggelos Economopoulos 		}
17868892ea20SAggelos Economopoulos 		busdma_seg_cnt--;
17878892ea20SAggelos Economopoulos 		seg++;
17888892ea20SAggelos Economopoulos 	}
17898892ea20SAggelos Economopoulos 	(req - rdma_count)->rdma_count = rdma_count;
17908892ea20SAggelos Economopoulos 
17918892ea20SAggelos Economopoulos 	do {
17928892ea20SAggelos Economopoulos 		req--;
17938892ea20SAggelos Economopoulos 		req->flags |= MXGEFW_FLAGS_TSO_LAST;
17948892ea20SAggelos Economopoulos 	} while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST)));
17958892ea20SAggelos Economopoulos 
179648d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
179748d12a0bSSepherosa Ziehau 
179848d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
179948d12a0bSSepherosa Ziehau 	info_last->map = map;
180048d12a0bSSepherosa Ziehau 	info_last->m = m;
180148d12a0bSSepherosa Ziehau 
18028892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
18030654769eSSepherosa Ziehau 
1804aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1805aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1806aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1807aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1808aca8f373SSepherosa Ziehau 		tx->activate++;
1809aca8f373SSepherosa Ziehau 		wmb();
1810aca8f373SSepherosa Ziehau 	}
1811ca8ca004SSepherosa Ziehau 	return 0;
18128892ea20SAggelos Economopoulos 
18138892ea20SAggelos Economopoulos drop:
18148892ea20SAggelos Economopoulos 	bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map);
18158892ea20SAggelos Economopoulos 	m_freem(m);
1816ca8ca004SSepherosa Ziehau 	return ENOBUFS;
18178892ea20SAggelos Economopoulos }
18188892ea20SAggelos Economopoulos 
1819ca8ca004SSepherosa Ziehau static int
mxge_encap(mxge_tx_ring_t * tx,struct mbuf * m,bus_addr_t zeropad)18205da1e9c3SSepherosa Ziehau mxge_encap(mxge_tx_ring_t *tx, struct mbuf *m, bus_addr_t zeropad)
18218892ea20SAggelos Economopoulos {
18228892ea20SAggelos Economopoulos 	mcp_kreq_ether_send_t *req;
18238892ea20SAggelos Economopoulos 	bus_dma_segment_t *seg;
182448d12a0bSSepherosa Ziehau 	bus_dmamap_t map;
182589d55360SSepherosa Ziehau 	int cnt, cum_len, err, i, idx, odd_flag;
18268892ea20SAggelos Economopoulos 	uint16_t pseudo_hdr_offset;
18278892ea20SAggelos Economopoulos 	uint8_t flags, cksum_offset;
1828fb39a573SSepherosa Ziehau 	struct mxge_buffer_state *info_map, *info_last;
18298892ea20SAggelos Economopoulos 
183089d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
1831ca8ca004SSepherosa Ziehau 		err = mxge_pullup_tso(&m);
1832ca8ca004SSepherosa Ziehau 		if (__predict_false(err))
1833ca8ca004SSepherosa Ziehau 			return err;
18348892ea20SAggelos Economopoulos 	}
183589d55360SSepherosa Ziehau 
18365ca32f31SSepherosa Ziehau 	/*
18375ca32f31SSepherosa Ziehau 	 * Map the frame for DMA
18385ca32f31SSepherosa Ziehau 	 */
183989d55360SSepherosa Ziehau 	idx = tx->req & tx->mask;
184048d12a0bSSepherosa Ziehau 	info_map = &tx->info[idx];
184148d12a0bSSepherosa Ziehau 	map = info_map->map;
184248d12a0bSSepherosa Ziehau 
184348d12a0bSSepherosa Ziehau 	err = bus_dmamap_load_mbuf_defrag(tx->dmat, map, &m,
184489d55360SSepherosa Ziehau 	    tx->seg_list, tx->max_desc - 2, &cnt, BUS_DMA_NOWAIT);
184589d55360SSepherosa Ziehau 	if (__predict_false(err != 0))
184689d55360SSepherosa Ziehau 		goto drop;
184748d12a0bSSepherosa Ziehau 	bus_dmamap_sync(tx->dmat, map, BUS_DMASYNC_PREWRITE);
184889d55360SSepherosa Ziehau 
18495ca32f31SSepherosa Ziehau 	/*
18505ca32f31SSepherosa Ziehau 	 * TSO is different enough, we handle it in another routine
18515ca32f31SSepherosa Ziehau 	 */
1852ca8ca004SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO)
185348d12a0bSSepherosa Ziehau 		return mxge_encap_tso(tx, info_map, m, cnt);
18548892ea20SAggelos Economopoulos 
18558892ea20SAggelos Economopoulos 	req = tx->req_list;
18568892ea20SAggelos Economopoulos 	cksum_offset = 0;
18578892ea20SAggelos Economopoulos 	pseudo_hdr_offset = 0;
18588892ea20SAggelos Economopoulos 	flags = MXGEFW_FLAGS_NO_TSO;
18598892ea20SAggelos Economopoulos 
18605ca32f31SSepherosa Ziehau 	/*
18615ca32f31SSepherosa Ziehau 	 * Checksum offloading
18625ca32f31SSepherosa Ziehau 	 */
186389d55360SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
186489d55360SSepherosa Ziehau 		cksum_offset = m->m_pkthdr.csum_lhlen + m->m_pkthdr.csum_iphlen;
18658892ea20SAggelos Economopoulos 		pseudo_hdr_offset = cksum_offset +  m->m_pkthdr.csum_data;
18668892ea20SAggelos Economopoulos 		pseudo_hdr_offset = htobe16(pseudo_hdr_offset);
18678892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18688892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_CKSUM;
18698892ea20SAggelos Economopoulos 		odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
18708892ea20SAggelos Economopoulos 	} else {
18718892ea20SAggelos Economopoulos 		odd_flag = 0;
18728892ea20SAggelos Economopoulos 	}
18738892ea20SAggelos Economopoulos 	if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE)
18748892ea20SAggelos Economopoulos 		flags |= MXGEFW_FLAGS_SMALL;
18758892ea20SAggelos Economopoulos 
18765ca32f31SSepherosa Ziehau 	/*
18775ca32f31SSepherosa Ziehau 	 * Convert segments into a request list
18785ca32f31SSepherosa Ziehau 	 */
18798892ea20SAggelos Economopoulos 	cum_len = 0;
18808892ea20SAggelos Economopoulos 	seg = tx->seg_list;
18818892ea20SAggelos Economopoulos 	req->flags = MXGEFW_FLAGS_FIRST;
18828892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
18835ca32f31SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr));
18845ca32f31SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr));
18858892ea20SAggelos Economopoulos 		req->length = htobe16(seg->ds_len);
18868892ea20SAggelos Economopoulos 		req->cksum_offset = cksum_offset;
18878892ea20SAggelos Economopoulos 		if (cksum_offset > seg->ds_len)
18888892ea20SAggelos Economopoulos 			cksum_offset -= seg->ds_len;
18898892ea20SAggelos Economopoulos 		else
18908892ea20SAggelos Economopoulos 			cksum_offset = 0;
18918892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
18928892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
18938892ea20SAggelos Economopoulos 		req->rdma_count = 1;
18948892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
18958892ea20SAggelos Economopoulos 		cum_len += seg->ds_len;
18968892ea20SAggelos Economopoulos 		seg++;
18978892ea20SAggelos Economopoulos 		req++;
18988892ea20SAggelos Economopoulos 		req->flags = 0;
18998892ea20SAggelos Economopoulos 	}
19008892ea20SAggelos Economopoulos 	req--;
19015ca32f31SSepherosa Ziehau 
19025ca32f31SSepherosa Ziehau 	/*
19035ca32f31SSepherosa Ziehau 	 * Pad runt to 60 bytes
19045ca32f31SSepherosa Ziehau 	 */
19058892ea20SAggelos Economopoulos 	if (cum_len < 60) {
19068892ea20SAggelos Economopoulos 		req++;
19075da1e9c3SSepherosa Ziehau 		req->addr_low = htobe32(MXGE_LOWPART_TO_U32(zeropad));
19085da1e9c3SSepherosa Ziehau 		req->addr_high = htobe32(MXGE_HIGHPART_TO_U32(zeropad));
19098892ea20SAggelos Economopoulos 		req->length = htobe16(60 - cum_len);
19108892ea20SAggelos Economopoulos 		req->cksum_offset = 0;
19118892ea20SAggelos Economopoulos 		req->pseudo_hdr_offset = pseudo_hdr_offset;
19128892ea20SAggelos Economopoulos 		req->pad = 0; /* complete solid 16-byte block */
19138892ea20SAggelos Economopoulos 		req->rdma_count = 1;
19148892ea20SAggelos Economopoulos 		req->flags |= flags | ((cum_len & 1) * odd_flag);
19158892ea20SAggelos Economopoulos 		cnt++;
19168892ea20SAggelos Economopoulos 	}
19178892ea20SAggelos Economopoulos 
19188892ea20SAggelos Economopoulos 	tx->req_list[0].rdma_count = cnt;
19198892ea20SAggelos Economopoulos #if 0
19208892ea20SAggelos Economopoulos 	/* print what the firmware will see */
19218892ea20SAggelos Economopoulos 	for (i = 0; i < cnt; i++) {
19226c348da6SAggelos Economopoulos 		kprintf("%d: addr: 0x%x 0x%x len:%d pso%d,"
19238892ea20SAggelos Economopoulos 		    "cso:%d, flags:0x%x, rdma:%d\n",
19248892ea20SAggelos Economopoulos 		    i, (int)ntohl(tx->req_list[i].addr_high),
19258892ea20SAggelos Economopoulos 		    (int)ntohl(tx->req_list[i].addr_low),
19268892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].length),
19278892ea20SAggelos Economopoulos 		    (int)ntohs(tx->req_list[i].pseudo_hdr_offset),
19288892ea20SAggelos Economopoulos 		    tx->req_list[i].cksum_offset, tx->req_list[i].flags,
19298892ea20SAggelos Economopoulos 		    tx->req_list[i].rdma_count);
19308892ea20SAggelos Economopoulos 	}
19316c348da6SAggelos Economopoulos 	kprintf("--------------\n");
19328892ea20SAggelos Economopoulos #endif
193348d12a0bSSepherosa Ziehau 	info_last = &tx->info[((cnt - 1) + tx->req) & tx->mask];
193448d12a0bSSepherosa Ziehau 
193548d12a0bSSepherosa Ziehau 	info_map->map = info_last->map;
193648d12a0bSSepherosa Ziehau 	info_last->map = map;
193748d12a0bSSepherosa Ziehau 	info_last->m = m;
193848d12a0bSSepherosa Ziehau 
19398892ea20SAggelos Economopoulos 	mxge_submit_req(tx, tx->req_list, cnt);
19400654769eSSepherosa Ziehau 
1941aca8f373SSepherosa Ziehau 	if (tx->send_go != NULL && tx->queue_active == 0) {
1942aca8f373SSepherosa Ziehau 		/* Tell the NIC to start polling this slice */
1943aca8f373SSepherosa Ziehau 		*tx->send_go = 1;
1944aca8f373SSepherosa Ziehau 		tx->queue_active = 1;
1945aca8f373SSepherosa Ziehau 		tx->activate++;
1946aca8f373SSepherosa Ziehau 		wmb();
1947aca8f373SSepherosa Ziehau 	}
1948ca8ca004SSepherosa Ziehau 	return 0;
19498892ea20SAggelos Economopoulos 
19508892ea20SAggelos Economopoulos drop:
19518892ea20SAggelos Economopoulos 	m_freem(m);
1952ca8ca004SSepherosa Ziehau 	return err;
19538892ea20SAggelos Economopoulos }
19548892ea20SAggelos Economopoulos 
19558892ea20SAggelos Economopoulos static void
mxge_start(struct ifnet * ifp,struct ifaltq_subque * ifsq)1956f0a26983SSepherosa Ziehau mxge_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
19578892ea20SAggelos Economopoulos {
19588892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
1959aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
19605da1e9c3SSepherosa Ziehau 	bus_addr_t zeropad;
1961ca8ca004SSepherosa Ziehau 	int encap = 0;
19628892ea20SAggelos Economopoulos 
1963aca8f373SSepherosa Ziehau 	KKASSERT(tx->ifsq == ifsq);
196426634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
1965795c96bbSSepherosa Ziehau 
1966795c96bbSSepherosa Ziehau 	if ((ifp->if_flags & IFF_RUNNING) == 0 || ifsq_is_oactive(ifsq))
1967795c96bbSSepherosa Ziehau 		return;
1968795c96bbSSepherosa Ziehau 
19695da1e9c3SSepherosa Ziehau 	zeropad = sc->zeropad_dma.dmem_busaddr;
1970795c96bbSSepherosa Ziehau 	while (tx->mask - (tx->req - tx->done) > tx->max_desc) {
1971795c96bbSSepherosa Ziehau 		struct mbuf *m;
1972ca8ca004SSepherosa Ziehau 		int error;
1973795c96bbSSepherosa Ziehau 
1974795c96bbSSepherosa Ziehau 		m = ifsq_dequeue(ifsq);
1975795c96bbSSepherosa Ziehau 		if (m == NULL)
1976ca8ca004SSepherosa Ziehau 			goto done;
1977795c96bbSSepherosa Ziehau 
1978795c96bbSSepherosa Ziehau 		BPF_MTAP(ifp, m);
19795da1e9c3SSepherosa Ziehau 		error = mxge_encap(tx, m, zeropad);
1980ca8ca004SSepherosa Ziehau 		if (!error)
1981ca8ca004SSepherosa Ziehau 			encap = 1;
1982fed54363SSepherosa Ziehau 		else
1983fed54363SSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
1984795c96bbSSepherosa Ziehau 	}
1985795c96bbSSepherosa Ziehau 
1986795c96bbSSepherosa Ziehau 	/* Ran out of transmit slots */
1987795c96bbSSepherosa Ziehau 	ifsq_set_oactive(ifsq);
1988ca8ca004SSepherosa Ziehau done:
1989ca8ca004SSepherosa Ziehau 	if (encap)
1990*e2292763SMatthew Dillon 		ifsq_watchdog_set_count(&tx->watchdog, 5);
1991ca8ca004SSepherosa Ziehau }
1992ca8ca004SSepherosa Ziehau 
1993ca8ca004SSepherosa Ziehau static void
mxge_watchdog(struct ifaltq_subque * ifsq)1994aca8f373SSepherosa Ziehau mxge_watchdog(struct ifaltq_subque *ifsq)
1995ca8ca004SSepherosa Ziehau {
1996aca8f373SSepherosa Ziehau 	struct ifnet *ifp = ifsq_get_ifp(ifsq);
1997ca8ca004SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
1998ca8ca004SSepherosa Ziehau 	uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause);
1999aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = ifsq_get_priv(ifsq);
2000ca8ca004SSepherosa Ziehau 
200126634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
2002ca8ca004SSepherosa Ziehau 
2003ca8ca004SSepherosa Ziehau 	/* Check for pause blocking before resetting */
2004ca8ca004SSepherosa Ziehau 	if (tx->watchdog_rx_pause == rx_pause) {
2005ca8ca004SSepherosa Ziehau 		mxge_warn_stuck(sc, tx, 0);
2006ca8ca004SSepherosa Ziehau 		mxge_watchdog_reset(sc);
2007ca8ca004SSepherosa Ziehau 		return;
2008ca8ca004SSepherosa Ziehau 	} else {
2009ca8ca004SSepherosa Ziehau 		if_printf(ifp, "Flow control blocking xmits, "
2010ca8ca004SSepherosa Ziehau 		    "check link partner\n");
2011ca8ca004SSepherosa Ziehau 	}
2012ca8ca004SSepherosa Ziehau 	tx->watchdog_rx_pause = rx_pause;
20138892ea20SAggelos Economopoulos }
20148892ea20SAggelos Economopoulos 
20158892ea20SAggelos Economopoulos /*
20162f47b54fSSepherosa Ziehau  * Copy an array of mcp_kreq_ether_recv_t's to the mcp.  Copy
20178892ea20SAggelos Economopoulos  * at most 32 bytes at a time, so as to avoid involving the software
20188892ea20SAggelos Economopoulos  * pio handler in the nic.  We re-write the first segment's low
20198892ea20SAggelos Economopoulos  * DMA address to mark it valid only after we write the entire chunk
20208892ea20SAggelos Economopoulos  * in a burst
20218892ea20SAggelos Economopoulos  */
2022ddbf91b7SSepherosa Ziehau static __inline void
mxge_submit_8rx(volatile mcp_kreq_ether_recv_t * dst,mcp_kreq_ether_recv_t * src)20238892ea20SAggelos Economopoulos mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst,
20248892ea20SAggelos Economopoulos     mcp_kreq_ether_recv_t *src)
20258892ea20SAggelos Economopoulos {
20268892ea20SAggelos Economopoulos 	uint32_t low;
20278892ea20SAggelos Economopoulos 
20288892ea20SAggelos Economopoulos 	low = src->addr_low;
20298892ea20SAggelos Economopoulos 	src->addr_low = 0xffffffff;
20308892ea20SAggelos Economopoulos 	mxge_pio_copy(dst, src, 4 * sizeof (*src));
20318892ea20SAggelos Economopoulos 	wmb();
20328892ea20SAggelos Economopoulos 	mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src));
20338892ea20SAggelos Economopoulos 	wmb();
20348892ea20SAggelos Economopoulos 	src->addr_low = low;
20358892ea20SAggelos Economopoulos 	dst->addr_low = low;
20368892ea20SAggelos Economopoulos 	wmb();
20378892ea20SAggelos Economopoulos }
20388892ea20SAggelos Economopoulos 
20398892ea20SAggelos Economopoulos static int
mxge_get_buf_small(mxge_rx_ring_t * rx,bus_dmamap_t map,int idx,boolean_t init)20408ebf015eSSepherosa Ziehau mxge_get_buf_small(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
20418ebf015eSSepherosa Ziehau     boolean_t init)
20428892ea20SAggelos Economopoulos {
20438892ea20SAggelos Economopoulos 	bus_dma_segment_t seg;
20448892ea20SAggelos Economopoulos 	struct mbuf *m;
2045363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
20468892ea20SAggelos Economopoulos 
2047b5523eacSSascha Wildner 	mflag = M_NOWAIT;
20488ebf015eSSepherosa Ziehau 	if (__predict_false(init))
2049b5523eacSSascha Wildner 		mflag = M_WAITOK;
20508ebf015eSSepherosa Ziehau 
20518ebf015eSSepherosa Ziehau 	m = m_gethdr(mflag, MT_DATA);
20528892ea20SAggelos Economopoulos 	if (m == NULL) {
20538892ea20SAggelos Economopoulos 		err = ENOBUFS;
20548ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20558ebf015eSSepherosa Ziehau 			/*
20568ebf015eSSepherosa Ziehau 			 * During initialization, there
20578ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20588ebf015eSSepherosa Ziehau 			 */
20598ebf015eSSepherosa Ziehau 			return err;
20608ebf015eSSepherosa Ziehau 		}
20618892ea20SAggelos Economopoulos 		goto done;
20628892ea20SAggelos Economopoulos 	}
20632823b018SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = MHLEN;
20648ebf015eSSepherosa Ziehau 
20657d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
20667d8771d4SAggelos Economopoulos 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
20678892ea20SAggelos Economopoulos 	if (err != 0) {
20688ebf015eSSepherosa Ziehau 		m_freem(m);
20698ebf015eSSepherosa Ziehau 		if (__predict_false(init)) {
20708ebf015eSSepherosa Ziehau 			/*
20718ebf015eSSepherosa Ziehau 			 * During initialization, there
20728ebf015eSSepherosa Ziehau 			 * is nothing to setup; bail out
20738ebf015eSSepherosa Ziehau 			 */
20748ebf015eSSepherosa Ziehau 			return err;
20758ebf015eSSepherosa Ziehau 		}
20768892ea20SAggelos Economopoulos 		goto done;
20778892ea20SAggelos Economopoulos 	}
20788ebf015eSSepherosa Ziehau 
20798892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
20808ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
20818ebf015eSSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
20828892ea20SAggelos Economopoulos 
20838892ea20SAggelos Economopoulos done:
20848892ea20SAggelos Economopoulos 	if ((idx & 7) == 7)
20858892ea20SAggelos Economopoulos 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
20868892ea20SAggelos Economopoulos 	return err;
20878892ea20SAggelos Economopoulos }
20888892ea20SAggelos Economopoulos 
20898892ea20SAggelos Economopoulos static int
mxge_get_buf_big(mxge_rx_ring_t * rx,bus_dmamap_t map,int idx,boolean_t init)2090363b44f8SSepherosa Ziehau mxge_get_buf_big(mxge_rx_ring_t *rx, bus_dmamap_t map, int idx,
2091363b44f8SSepherosa Ziehau     boolean_t init)
20928892ea20SAggelos Economopoulos {
2093b9a8961fSSepherosa Ziehau 	bus_dma_segment_t seg;
20948892ea20SAggelos Economopoulos 	struct mbuf *m;
2095363b44f8SSepherosa Ziehau 	int cnt, err, mflag;
2096363b44f8SSepherosa Ziehau 
2097b5523eacSSascha Wildner 	mflag = M_NOWAIT;
2098363b44f8SSepherosa Ziehau 	if (__predict_false(init))
2099b5523eacSSascha Wildner 		mflag = M_WAITOK;
21008892ea20SAggelos Economopoulos 
21018892ea20SAggelos Economopoulos 	if (rx->cl_size == MCLBYTES)
2102363b44f8SSepherosa Ziehau 		m = m_getcl(mflag, MT_DATA, M_PKTHDR);
2103b9a8961fSSepherosa Ziehau 	else
2104363b44f8SSepherosa Ziehau 		m = m_getjcl(mflag, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
21058892ea20SAggelos Economopoulos 	if (m == NULL) {
21068892ea20SAggelos Economopoulos 		err = ENOBUFS;
2107363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2108363b44f8SSepherosa Ziehau 			/*
2109363b44f8SSepherosa Ziehau 			 * During initialization, there
2110363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2111363b44f8SSepherosa Ziehau 			 */
2112363b44f8SSepherosa Ziehau 			return err;
2113363b44f8SSepherosa Ziehau 		}
21148892ea20SAggelos Economopoulos 		goto done;
21158892ea20SAggelos Economopoulos 	}
21163ae50fbeSSepherosa Ziehau 	m->m_len = m->m_pkthdr.len = rx->cl_size;
2117b9a8961fSSepherosa Ziehau 
21187d8771d4SAggelos Economopoulos 	err = bus_dmamap_load_mbuf_segment(rx->dmat, map, m,
2119b9a8961fSSepherosa Ziehau 	    &seg, 1, &cnt, BUS_DMA_NOWAIT);
21208892ea20SAggelos Economopoulos 	if (err != 0) {
2121363b44f8SSepherosa Ziehau 		m_freem(m);
2122363b44f8SSepherosa Ziehau 		if (__predict_false(init)) {
2123363b44f8SSepherosa Ziehau 			/*
2124363b44f8SSepherosa Ziehau 			 * During initialization, there
2125363b44f8SSepherosa Ziehau 			 * is nothing to setup; bail out
2126363b44f8SSepherosa Ziehau 			 */
2127363b44f8SSepherosa Ziehau 			return err;
2128363b44f8SSepherosa Ziehau 		}
21298892ea20SAggelos Economopoulos 		goto done;
21308892ea20SAggelos Economopoulos 	}
2131b9a8961fSSepherosa Ziehau 
21328892ea20SAggelos Economopoulos 	rx->info[idx].m = m;
2133363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_low = htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr));
2134363b44f8SSepherosa Ziehau 	rx->shadow[idx].addr_high = htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr));
21358892ea20SAggelos Economopoulos 
21368892ea20SAggelos Economopoulos done:
2137b9a8961fSSepherosa Ziehau 	if ((idx & 7) == 7)
2138b9a8961fSSepherosa Ziehau 		mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]);
21398892ea20SAggelos Economopoulos 	return err;
21408892ea20SAggelos Economopoulos }
21418892ea20SAggelos Economopoulos 
21428892ea20SAggelos Economopoulos /*
21438892ea20SAggelos Economopoulos  * Myri10GE hardware checksums are not valid if the sender
21448892ea20SAggelos Economopoulos  * padded the frame with non-zero padding.  This is because
21458892ea20SAggelos Economopoulos  * the firmware just does a simple 16-bit 1s complement
21468892ea20SAggelos Economopoulos  * checksum across the entire frame, excluding the first 14
21478892ea20SAggelos Economopoulos  * bytes.  It is best to simply to check the checksum and
21488892ea20SAggelos Economopoulos  * tell the stack about it only if the checksum is good
21498892ea20SAggelos Economopoulos  */
215052cf8dfcSSepherosa Ziehau static __inline uint16_t
mxge_rx_csum(struct mbuf * m,int csum)21518892ea20SAggelos Economopoulos mxge_rx_csum(struct mbuf *m, int csum)
21528892ea20SAggelos Economopoulos {
215352cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
215452cf8dfcSSepherosa Ziehau 	const struct ip *ip;
21558892ea20SAggelos Economopoulos 	uint16_t c;
21568892ea20SAggelos Economopoulos 
215752cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
21588892ea20SAggelos Economopoulos 
215952cf8dfcSSepherosa Ziehau 	/* Only deal with IPv4 TCP & UDP for now */
21608892ea20SAggelos Economopoulos 	if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP)))
21618892ea20SAggelos Economopoulos 		return 1;
216252cf8dfcSSepherosa Ziehau 
216352cf8dfcSSepherosa Ziehau 	ip = (const struct ip *)(eh + 1);
216452cf8dfcSSepherosa Ziehau 	if (__predict_false(ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP))
21658892ea20SAggelos Economopoulos 		return 1;
216652cf8dfcSSepherosa Ziehau 
21678892ea20SAggelos Economopoulos #ifdef INET
21688892ea20SAggelos Economopoulos 	c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
21698892ea20SAggelos Economopoulos 	    htonl(ntohs(csum) + ntohs(ip->ip_len) +
21708892ea20SAggelos Economopoulos 	          - (ip->ip_hl << 2) + ip->ip_p));
21718892ea20SAggelos Economopoulos #else
21728892ea20SAggelos Economopoulos 	c = 1;
21738892ea20SAggelos Economopoulos #endif
21748892ea20SAggelos Economopoulos 	c ^= 0xffff;
217552cf8dfcSSepherosa Ziehau 	return c;
21768892ea20SAggelos Economopoulos }
21778892ea20SAggelos Economopoulos 
21788892ea20SAggelos Economopoulos static void
mxge_vlan_tag_remove(struct mbuf * m,uint32_t * csum)21798892ea20SAggelos Economopoulos mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum)
21808892ea20SAggelos Economopoulos {
21818892ea20SAggelos Economopoulos 	struct ether_vlan_header *evl;
21828892ea20SAggelos Economopoulos 	uint32_t partial;
21838892ea20SAggelos Economopoulos 
21848892ea20SAggelos Economopoulos 	evl = mtod(m, struct ether_vlan_header *);
21858892ea20SAggelos Economopoulos 
21868892ea20SAggelos Economopoulos 	/*
218752cf8dfcSSepherosa Ziehau 	 * Fix checksum by subtracting EVL_ENCAPLEN bytes after
218852cf8dfcSSepherosa Ziehau 	 * what the firmware thought was the end of the ethernet
21898892ea20SAggelos Economopoulos 	 * header.
21908892ea20SAggelos Economopoulos 	 */
21918892ea20SAggelos Economopoulos 
219252cf8dfcSSepherosa Ziehau 	/* Put checksum into host byte order */
21938892ea20SAggelos Economopoulos 	*csum = ntohs(*csum);
21948892ea20SAggelos Economopoulos 
219552cf8dfcSSepherosa Ziehau 	partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN));
219652cf8dfcSSepherosa Ziehau 	*csum += ~partial;
219752cf8dfcSSepherosa Ziehau 	*csum += ((*csum) < ~partial);
219852cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
219952cf8dfcSSepherosa Ziehau 	*csum = ((*csum) >> 16) + ((*csum) & 0xFFFF);
220052cf8dfcSSepherosa Ziehau 
220152cf8dfcSSepherosa Ziehau 	/*
220252cf8dfcSSepherosa Ziehau 	 * Restore checksum to network byte order;
220352cf8dfcSSepherosa Ziehau 	 * later consumers expect this
220452cf8dfcSSepherosa Ziehau 	 */
22058892ea20SAggelos Economopoulos 	*csum = htons(*csum);
22068892ea20SAggelos Economopoulos 
22078892ea20SAggelos Economopoulos 	/* save the tag */
2208b915556eSAggelos Economopoulos 	m->m_pkthdr.ether_vlantag = ntohs(evl->evl_tag);
22098892ea20SAggelos Economopoulos 	m->m_flags |= M_VLANTAG;
22108892ea20SAggelos Economopoulos 
22118892ea20SAggelos Economopoulos 	/*
22128892ea20SAggelos Economopoulos 	 * Remove the 802.1q header by copying the Ethernet
22138892ea20SAggelos Economopoulos 	 * addresses over it and adjusting the beginning of
22148892ea20SAggelos Economopoulos 	 * the data in the mbuf.  The encapsulated Ethernet
22158892ea20SAggelos Economopoulos 	 * type field is already in place.
22168892ea20SAggelos Economopoulos 	 */
2217b915556eSAggelos Economopoulos 	bcopy((char *)evl, (char *)evl + EVL_ENCAPLEN,
22188892ea20SAggelos Economopoulos 	    ETHER_HDR_LEN - ETHER_TYPE_LEN);
2219b915556eSAggelos Economopoulos 	m_adj(m, EVL_ENCAPLEN);
22208892ea20SAggelos Economopoulos }
22218892ea20SAggelos Economopoulos 
22228892ea20SAggelos Economopoulos 
222352cf8dfcSSepherosa Ziehau static __inline void
mxge_rx_done_big(struct ifnet * ifp,mxge_rx_ring_t * rx,uint32_t len,uint32_t csum)22245da1e9c3SSepherosa Ziehau mxge_rx_done_big(struct ifnet *ifp, mxge_rx_ring_t *rx,
22255da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22268892ea20SAggelos Economopoulos {
22278892ea20SAggelos Economopoulos 	struct mbuf *m;
222852cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22298892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22308892ea20SAggelos Economopoulos 	int idx;
22318892ea20SAggelos Economopoulos 
22328892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
2233b9a8961fSSepherosa Ziehau 	rx->cnt++;
223452cf8dfcSSepherosa Ziehau 
223552cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22368892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
223752cf8dfcSSepherosa Ziehau 
223852cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
2239363b44f8SSepherosa Ziehau 	if (mxge_get_buf_big(rx, rx->extra_map, idx, FALSE)) {
224052cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2241d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22428892ea20SAggelos Economopoulos 		return;
22438892ea20SAggelos Economopoulos 	}
22448892ea20SAggelos Economopoulos 
224552cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
22468892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
22478892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
22488892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
22498892ea20SAggelos Economopoulos 
225052cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
22518892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
22528892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
22538892ea20SAggelos Economopoulos 
225452cf8dfcSSepherosa Ziehau 	/*
225552cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
225652cf8dfcSSepherosa Ziehau 	 * aligned
225752cf8dfcSSepherosa Ziehau 	 */
22588892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
22598892ea20SAggelos Economopoulos 
22608892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
22618892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
226252cf8dfcSSepherosa Ziehau 
2263cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
226452cf8dfcSSepherosa Ziehau 
226552cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
226652cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
22678892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
226852cf8dfcSSepherosa Ziehau 
226952cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
227089d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
227152cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
227289d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
22738892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
227489d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
227589d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
22768892ea20SAggelos Economopoulos 	}
227773029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
22788892ea20SAggelos Economopoulos }
22798892ea20SAggelos Economopoulos 
228052cf8dfcSSepherosa Ziehau static __inline void
mxge_rx_done_small(struct ifnet * ifp,mxge_rx_ring_t * rx,uint32_t len,uint32_t csum)22815da1e9c3SSepherosa Ziehau mxge_rx_done_small(struct ifnet *ifp, mxge_rx_ring_t *rx,
22825da1e9c3SSepherosa Ziehau     uint32_t len, uint32_t csum)
22838892ea20SAggelos Economopoulos {
228452cf8dfcSSepherosa Ziehau 	const struct ether_header *eh;
22858892ea20SAggelos Economopoulos 	struct mbuf *m;
22868892ea20SAggelos Economopoulos 	bus_dmamap_t old_map;
22878892ea20SAggelos Economopoulos 	int idx;
22888892ea20SAggelos Economopoulos 
22898892ea20SAggelos Economopoulos 	idx = rx->cnt & rx->mask;
22908892ea20SAggelos Economopoulos 	rx->cnt++;
229152cf8dfcSSepherosa Ziehau 
229252cf8dfcSSepherosa Ziehau 	/* Save a pointer to the received mbuf */
22938892ea20SAggelos Economopoulos 	m = rx->info[idx].m;
229452cf8dfcSSepherosa Ziehau 
229552cf8dfcSSepherosa Ziehau 	/* Try to replace the received mbuf */
22968ebf015eSSepherosa Ziehau 	if (mxge_get_buf_small(rx, rx->extra_map, idx, FALSE)) {
229752cf8dfcSSepherosa Ziehau 		/* Drop the frame -- the old mbuf is re-cycled */
2298d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ierrors, 1);
22998892ea20SAggelos Economopoulos 		return;
23008892ea20SAggelos Economopoulos 	}
23018892ea20SAggelos Economopoulos 
230252cf8dfcSSepherosa Ziehau 	/* Unmap the received buffer */
23038892ea20SAggelos Economopoulos 	old_map = rx->info[idx].map;
23048892ea20SAggelos Economopoulos 	bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD);
23058892ea20SAggelos Economopoulos 	bus_dmamap_unload(rx->dmat, old_map);
23068892ea20SAggelos Economopoulos 
230752cf8dfcSSepherosa Ziehau 	/* Swap the bus_dmamap_t's */
23088892ea20SAggelos Economopoulos 	rx->info[idx].map = rx->extra_map;
23098892ea20SAggelos Economopoulos 	rx->extra_map = old_map;
23108892ea20SAggelos Economopoulos 
231152cf8dfcSSepherosa Ziehau 	/*
231252cf8dfcSSepherosa Ziehau 	 * mcp implicitly skips 1st 2 bytes so that packet is properly
231352cf8dfcSSepherosa Ziehau 	 * aligned
231452cf8dfcSSepherosa Ziehau 	 */
23158892ea20SAggelos Economopoulos 	m->m_data += MXGEFW_PAD;
23168892ea20SAggelos Economopoulos 
23178892ea20SAggelos Economopoulos 	m->m_pkthdr.rcvif = ifp;
23188892ea20SAggelos Economopoulos 	m->m_len = m->m_pkthdr.len = len;
231952cf8dfcSSepherosa Ziehau 
2320cc9c62a4SSepherosa Ziehau 	IFNET_STAT_INC(ifp, ipackets, 1);
232152cf8dfcSSepherosa Ziehau 
232252cf8dfcSSepherosa Ziehau 	eh = mtod(m, const struct ether_header *);
232352cf8dfcSSepherosa Ziehau 	if (eh->ether_type == htons(ETHERTYPE_VLAN))
23248892ea20SAggelos Economopoulos 		mxge_vlan_tag_remove(m, &csum);
232552cf8dfcSSepherosa Ziehau 
232652cf8dfcSSepherosa Ziehau 	/* If the checksum is valid, mark it in the mbuf header */
232789d55360SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_RXCSUM) &&
232852cf8dfcSSepherosa Ziehau 	    mxge_rx_csum(m, csum) == 0) {
232989d55360SSepherosa Ziehau 		/* Tell the stack that the checksum is good */
23308892ea20SAggelos Economopoulos 		m->m_pkthdr.csum_data = 0xffff;
233189d55360SSepherosa Ziehau 		m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR |
233289d55360SSepherosa Ziehau 		    CSUM_DATA_VALID;
23338892ea20SAggelos Economopoulos 	}
233473029d08SFranco Fichtner 	ifp->if_input(ifp, m, NULL, -1);
23358892ea20SAggelos Economopoulos }
23368892ea20SAggelos Economopoulos 
233752cf8dfcSSepherosa Ziehau static __inline void
mxge_clean_rx_done(struct ifnet * ifp,struct mxge_rx_data * rx_data,int cycle)23382276707eSSepherosa Ziehau mxge_clean_rx_done(struct ifnet *ifp, struct mxge_rx_data *rx_data, int cycle)
23398892ea20SAggelos Economopoulos {
23405da1e9c3SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &rx_data->rx_done;
23415da1e9c3SSepherosa Ziehau 
23422276707eSSepherosa Ziehau 	while (rx_done->entry[rx_done->idx].length != 0 && cycle != 0) {
234352cf8dfcSSepherosa Ziehau 		uint16_t length, checksum;
234452cf8dfcSSepherosa Ziehau 
23458892ea20SAggelos Economopoulos 		length = ntohs(rx_done->entry[rx_done->idx].length);
23468892ea20SAggelos Economopoulos 		rx_done->entry[rx_done->idx].length = 0;
234752cf8dfcSSepherosa Ziehau 
23488892ea20SAggelos Economopoulos 		checksum = rx_done->entry[rx_done->idx].checksum;
234952cf8dfcSSepherosa Ziehau 
235060f8c66dSSepherosa Ziehau 		if (length <= MXGE_RX_SMALL_BUFLEN) {
23515da1e9c3SSepherosa Ziehau 			mxge_rx_done_small(ifp, &rx_data->rx_small,
23525da1e9c3SSepherosa Ziehau 			    length, checksum);
23535da1e9c3SSepherosa Ziehau 		} else {
23545da1e9c3SSepherosa Ziehau 			mxge_rx_done_big(ifp, &rx_data->rx_big,
23555da1e9c3SSepherosa Ziehau 			    length, checksum);
23565da1e9c3SSepherosa Ziehau 		}
235752cf8dfcSSepherosa Ziehau 
2358a3f51d6bSSepherosa Ziehau 		rx_done->idx++;
2359a3f51d6bSSepherosa Ziehau 		rx_done->idx &= rx_done->mask;
23602276707eSSepherosa Ziehau 		--cycle;
23618892ea20SAggelos Economopoulos 	}
23628892ea20SAggelos Economopoulos }
23638892ea20SAggelos Economopoulos 
2364ddbf91b7SSepherosa Ziehau static __inline void
mxge_tx_done(struct ifnet * ifp,mxge_tx_ring_t * tx,uint32_t mcp_idx)23655da1e9c3SSepherosa Ziehau mxge_tx_done(struct ifnet *ifp, mxge_tx_ring_t *tx, uint32_t mcp_idx)
23668892ea20SAggelos Economopoulos {
236726634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&tx->tx_serialize);
236866e7a0e8SSepherosa Ziehau 
23698892ea20SAggelos Economopoulos 	while (tx->pkt_done != mcp_idx) {
237066e7a0e8SSepherosa Ziehau 		struct mbuf *m;
237166e7a0e8SSepherosa Ziehau 		int idx;
237266e7a0e8SSepherosa Ziehau 
23738892ea20SAggelos Economopoulos 		idx = tx->done & tx->mask;
23748892ea20SAggelos Economopoulos 		tx->done++;
237566e7a0e8SSepherosa Ziehau 
23768892ea20SAggelos Economopoulos 		m = tx->info[idx].m;
237766e7a0e8SSepherosa Ziehau 		/*
237866e7a0e8SSepherosa Ziehau 		 * mbuf and DMA map only attached to the first
237966e7a0e8SSepherosa Ziehau 		 * segment per-mbuf.
238066e7a0e8SSepherosa Ziehau 		 */
23818892ea20SAggelos Economopoulos 		if (m != NULL) {
238248d12a0bSSepherosa Ziehau 			tx->pkt_done++;
2383cc9c62a4SSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
23848892ea20SAggelos Economopoulos 			tx->info[idx].m = NULL;
238566e7a0e8SSepherosa Ziehau 			bus_dmamap_unload(tx->dmat, tx->info[idx].map);
23868892ea20SAggelos Economopoulos 			m_freem(m);
23878892ea20SAggelos Economopoulos 		}
23888892ea20SAggelos Economopoulos 	}
23898892ea20SAggelos Economopoulos 
239066e7a0e8SSepherosa Ziehau 	/*
239166e7a0e8SSepherosa Ziehau 	 * If we have space, clear OACTIVE to tell the stack that
239266e7a0e8SSepherosa Ziehau 	 * its OK to send packets
239366e7a0e8SSepherosa Ziehau 	 */
239483c2b410SSepherosa Ziehau 	if (tx->req - tx->done < (tx->mask + 1) / 2) {
2395aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
2396aca8f373SSepherosa Ziehau 		if (tx->req == tx->done) {
2397aca8f373SSepherosa Ziehau 			/* Reset watchdog */
2398*e2292763SMatthew Dillon 			ifsq_watchdog_set_count(&tx->watchdog, 0);
2399aca8f373SSepherosa Ziehau 		}
2400ca8ca004SSepherosa Ziehau 	}
240189d55360SSepherosa Ziehau 
2402aca8f373SSepherosa Ziehau 	if (!ifsq_is_empty(tx->ifsq))
2403aca8f373SSepherosa Ziehau 		ifsq_devstart(tx->ifsq);
2404adb0d705SSepherosa Ziehau 
2405adb0d705SSepherosa Ziehau 	if (tx->send_stop != NULL && tx->req == tx->done) {
2406adb0d705SSepherosa Ziehau 		/*
2407adb0d705SSepherosa Ziehau 		 * Let the NIC stop polling this queue, since there
2408adb0d705SSepherosa Ziehau 		 * are no more transmits pending
2409adb0d705SSepherosa Ziehau 		 */
2410adb0d705SSepherosa Ziehau 		*tx->send_stop = 1;
2411adb0d705SSepherosa Ziehau 		tx->queue_active = 0;
2412adb0d705SSepherosa Ziehau 		tx->deactivate++;
2413adb0d705SSepherosa Ziehau 		wmb();
2414adb0d705SSepherosa Ziehau 	}
24158892ea20SAggelos Economopoulos }
24168892ea20SAggelos Economopoulos 
241789d55360SSepherosa Ziehau static struct mxge_media_type mxge_xfp_media_types[] = {
24188892ea20SAggelos Economopoulos 	{IFM_10G_CX4,	0x7f, 		"10GBASE-CX4 (module)"},
24198892ea20SAggelos Economopoulos 	{IFM_10G_SR, 	(1 << 7),	"10GBASE-SR"},
24208892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 6),	"10GBASE-LR"},
2421166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 5),	"10GBASE-ER"},
24228892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 4),	"10GBASE-LRM"},
2423166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 3),	"10GBASE-SW"},
2424166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 2),	"10GBASE-LW"},
2425166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 1),	"10GBASE-EW"},
2426166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 0),	"Reserved"}
24278892ea20SAggelos Economopoulos };
242889d55360SSepherosa Ziehau 
242989d55360SSepherosa Ziehau static struct mxge_media_type mxge_sfp_media_types[] = {
243089d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,      0,	"10GBASE-Twinax"},
2431166c46afSSepherosa Ziehau 	{IFM_NONE,	(1 << 7),	"Reserved"},
24328892ea20SAggelos Economopoulos 	{IFM_10G_LRM,	(1 << 6),	"10GBASE-LRM"},
24338892ea20SAggelos Economopoulos 	{IFM_10G_LR, 	(1 << 5),	"10GBASE-LR"},
243489d55360SSepherosa Ziehau 	{IFM_10G_SR,	(1 << 4),	"10GBASE-SR"},
243589d55360SSepherosa Ziehau 	{IFM_10G_TWINAX,(1 << 0),	"10GBASE-Twinax"}
24368892ea20SAggelos Economopoulos };
24378892ea20SAggelos Economopoulos 
24388892ea20SAggelos Economopoulos static void
mxge_media_set(mxge_softc_t * sc,int media_type)243989d55360SSepherosa Ziehau mxge_media_set(mxge_softc_t *sc, int media_type)
24408892ea20SAggelos Economopoulos {
244100f2de12SSepherosa Ziehau 	int fc_opt = 0;
244200f2de12SSepherosa Ziehau 
2443166c46afSSepherosa Ziehau 	if (media_type == IFM_NONE)
2444166c46afSSepherosa Ziehau 		return;
2445166c46afSSepherosa Ziehau 
244600f2de12SSepherosa Ziehau 	if (sc->pause)
244700f2de12SSepherosa Ziehau 		fc_opt = IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
244800f2de12SSepherosa Ziehau 
244900f2de12SSepherosa Ziehau 	ifmedia_add(&sc->media, MXGE_IFM | media_type, 0, NULL);
245000f2de12SSepherosa Ziehau 	ifmedia_set(&sc->media, MXGE_IFM | media_type | fc_opt);
245100f2de12SSepherosa Ziehau 
245289d55360SSepherosa Ziehau 	sc->current_media = media_type;
2453166c46afSSepherosa Ziehau }
2454166c46afSSepherosa Ziehau 
2455166c46afSSepherosa Ziehau static void
mxge_media_unset(mxge_softc_t * sc)2456166c46afSSepherosa Ziehau mxge_media_unset(mxge_softc_t *sc)
2457166c46afSSepherosa Ziehau {
2458166c46afSSepherosa Ziehau 	ifmedia_removeall(&sc->media);
2459166c46afSSepherosa Ziehau 	sc->current_media = IFM_NONE;
24608892ea20SAggelos Economopoulos }
24618892ea20SAggelos Economopoulos 
24628892ea20SAggelos Economopoulos static void
mxge_media_init(mxge_softc_t * sc)246389d55360SSepherosa Ziehau mxge_media_init(mxge_softc_t *sc)
24648892ea20SAggelos Economopoulos {
2465c7431c78SSepherosa Ziehau 	const char *ptr;
246689d55360SSepherosa Ziehau 	int i;
24678892ea20SAggelos Economopoulos 
2468166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
24698892ea20SAggelos Economopoulos 
24708892ea20SAggelos Economopoulos 	/*
24712f47b54fSSepherosa Ziehau 	 * Parse the product code to deterimine the interface type
24728892ea20SAggelos Economopoulos 	 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
24738892ea20SAggelos Economopoulos 	 * after the 3rd dash in the driver's cached copy of the
24748892ea20SAggelos Economopoulos 	 * EEPROM's product code string.
24758892ea20SAggelos Economopoulos 	 */
24768892ea20SAggelos Economopoulos 	ptr = sc->product_code_string;
24778892ea20SAggelos Economopoulos 	if (ptr == NULL) {
2478af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Missing product code\n");
247989d55360SSepherosa Ziehau 		return;
24808892ea20SAggelos Economopoulos 	}
24818892ea20SAggelos Economopoulos 
24828892ea20SAggelos Economopoulos 	for (i = 0; i < 3; i++, ptr++) {
248389d55360SSepherosa Ziehau 		ptr = strchr(ptr, '-');
24848892ea20SAggelos Economopoulos 		if (ptr == NULL) {
2485af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "only %d dashes in PC?!?\n", i);
24868892ea20SAggelos Economopoulos 			return;
24878892ea20SAggelos Economopoulos 		}
24888892ea20SAggelos Economopoulos 	}
248989d55360SSepherosa Ziehau 	if (*ptr == 'C' || *(ptr +1) == 'C') {
24908892ea20SAggelos Economopoulos 		/* -C is CX4 */
249189d55360SSepherosa Ziehau 		sc->connector = MXGE_CX4;
249289d55360SSepherosa Ziehau 		mxge_media_set(sc, IFM_10G_CX4);
249389d55360SSepherosa Ziehau 	} else if (*ptr == 'Q') {
24948892ea20SAggelos Economopoulos 		/* -Q is Quad Ribbon Fiber */
249589d55360SSepherosa Ziehau 		sc->connector = MXGE_QRF;
2496af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Quad Ribbon Fiber Media\n");
24972f47b54fSSepherosa Ziehau 		/* DragonFly has no media type for Quad ribbon fiber */
249889d55360SSepherosa Ziehau 	} else if (*ptr == 'R') {
249989d55360SSepherosa Ziehau 		/* -R is XFP */
250089d55360SSepherosa Ziehau 		sc->connector = MXGE_XFP;
2501166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
250289d55360SSepherosa Ziehau 	} else if (*ptr == 'S' || *(ptr +1) == 'S') {
250389d55360SSepherosa Ziehau 		/* -S or -2S is SFP+ */
250489d55360SSepherosa Ziehau 		sc->connector = MXGE_SFP;
2505166c46afSSepherosa Ziehau 		/* NOTE: ifmedia will be installed later */
250689d55360SSepherosa Ziehau 	} else {
2507166c46afSSepherosa Ziehau 		sc->connector = MXGE_UNK;
2508af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "Unknown media type: %c\n", *ptr);
250989d55360SSepherosa Ziehau 	}
25108892ea20SAggelos Economopoulos }
25118892ea20SAggelos Economopoulos 
251289d55360SSepherosa Ziehau /*
251389d55360SSepherosa Ziehau  * Determine the media type for a NIC.  Some XFPs will identify
251489d55360SSepherosa Ziehau  * themselves only when their link is up, so this is initiated via a
251589d55360SSepherosa Ziehau  * link up interrupt.  However, this can potentially take up to
251689d55360SSepherosa Ziehau  * several milliseconds, so it is run via the watchdog routine, rather
251789d55360SSepherosa Ziehau  * than in the interrupt handler itself.
251889d55360SSepherosa Ziehau  */
251989d55360SSepherosa Ziehau static void
mxge_media_probe(mxge_softc_t * sc)252089d55360SSepherosa Ziehau mxge_media_probe(mxge_softc_t *sc)
252189d55360SSepherosa Ziehau {
252289d55360SSepherosa Ziehau 	mxge_cmd_t cmd;
25237cc92483SSepherosa Ziehau 	const char *cage_type;
252489d55360SSepherosa Ziehau 	struct mxge_media_type *mxge_media_types = NULL;
252589d55360SSepherosa Ziehau 	int i, err, ms, mxge_media_type_entries;
252689d55360SSepherosa Ziehau 	uint32_t byte;
252789d55360SSepherosa Ziehau 
252889d55360SSepherosa Ziehau 	sc->need_media_probe = 0;
252989d55360SSepherosa Ziehau 
253089d55360SSepherosa Ziehau 	if (sc->connector == MXGE_XFP) {
25318892ea20SAggelos Economopoulos 		/* -R is XFP */
25328892ea20SAggelos Economopoulos 		mxge_media_types = mxge_xfp_media_types;
2533b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_xfp_media_types);
25348892ea20SAggelos Economopoulos 		byte = MXGE_XFP_COMPLIANCE_BYTE;
25358892ea20SAggelos Economopoulos 		cage_type = "XFP";
253689d55360SSepherosa Ziehau 	} else 	if (sc->connector == MXGE_SFP) {
25378892ea20SAggelos Economopoulos 		/* -S or -2S is SFP+ */
25388892ea20SAggelos Economopoulos 		mxge_media_types = mxge_sfp_media_types;
2539b22bdff4SSascha Wildner 		mxge_media_type_entries = NELEM(mxge_sfp_media_types);
25408892ea20SAggelos Economopoulos 		cage_type = "SFP+";
25418892ea20SAggelos Economopoulos 		byte = 3;
254289d55360SSepherosa Ziehau 	} else {
254389d55360SSepherosa Ziehau 		/* nothing to do; media type cannot change */
25448892ea20SAggelos Economopoulos 		return;
25458892ea20SAggelos Economopoulos 	}
25468892ea20SAggelos Economopoulos 
25478892ea20SAggelos Economopoulos 	/*
25488892ea20SAggelos Economopoulos 	 * At this point we know the NIC has an XFP cage, so now we
25498892ea20SAggelos Economopoulos 	 * try to determine what is in the cage by using the
25508892ea20SAggelos Economopoulos 	 * firmware's XFP I2C commands to read the XFP 10GbE compilance
25518892ea20SAggelos Economopoulos 	 * register.  We read just one byte, which may take over
25528892ea20SAggelos Economopoulos 	 * a millisecond
25538892ea20SAggelos Economopoulos 	 */
25548892ea20SAggelos Economopoulos 
255557e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
25568892ea20SAggelos Economopoulos 	cmd.data0 = 0;	 /* just fetch 1 byte, not all 256 */
25578892ea20SAggelos Economopoulos 	cmd.data1 = byte;
25588892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd);
2559166c46afSSepherosa Ziehau 	if (err != MXGEFW_CMD_OK) {
25607cc92483SSepherosa Ziehau 		if (err == MXGEFW_CMD_ERROR_I2C_FAILURE)
2561af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "failed to read XFP\n");
2562166c46afSSepherosa Ziehau 		else if (err == MXGEFW_CMD_ERROR_I2C_ABSENT)
2563af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "Type R/S with no XFP!?!?\n");
2564166c46afSSepherosa Ziehau 		else
2565166c46afSSepherosa Ziehau 			if_printf(sc->ifp, "I2C read failed, err: %d", err);
2566166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25678892ea20SAggelos Economopoulos 		return;
2568166c46afSSepherosa Ziehau 	}
25698892ea20SAggelos Economopoulos 
25707cc92483SSepherosa Ziehau 	/* Now we wait for the data to be cached */
25718892ea20SAggelos Economopoulos 	cmd.data0 = byte;
25728892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25737cc92483SSepherosa Ziehau 	for (ms = 0; err == EBUSY && ms < 50; ms++) {
25748892ea20SAggelos Economopoulos 		DELAY(1000);
25758892ea20SAggelos Economopoulos 		cmd.data0 = byte;
25768892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd);
25778892ea20SAggelos Economopoulos 	}
25788892ea20SAggelos Economopoulos 	if (err != MXGEFW_CMD_OK) {
2579af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "failed to read %s (%d, %dms)\n",
25808892ea20SAggelos Economopoulos 		    cage_type, err, ms);
2581166c46afSSepherosa Ziehau 		mxge_media_unset(sc);
25828892ea20SAggelos Economopoulos 		return;
25838892ea20SAggelos Economopoulos 	}
25848892ea20SAggelos Economopoulos 
25858892ea20SAggelos Economopoulos 	if (cmd.data0 == mxge_media_types[0].bitmask) {
25867cc92483SSepherosa Ziehau 		if (bootverbose) {
2587af85d4d5SSepherosa Ziehau 			if_printf(sc->ifp, "%s:%s\n", cage_type,
25888892ea20SAggelos Economopoulos 			    mxge_media_types[0].name);
25897cc92483SSepherosa Ziehau 		}
259089d55360SSepherosa Ziehau 		if (sc->current_media != mxge_media_types[0].flag) {
2591166c46afSSepherosa Ziehau 			mxge_media_unset(sc);
259289d55360SSepherosa Ziehau 			mxge_media_set(sc, mxge_media_types[0].flag);
259389d55360SSepherosa Ziehau 		}
25948892ea20SAggelos Economopoulos 		return;
25958892ea20SAggelos Economopoulos 	}
25968892ea20SAggelos Economopoulos 	for (i = 1; i < mxge_media_type_entries; i++) {
25978892ea20SAggelos Economopoulos 		if (cmd.data0 & mxge_media_types[i].bitmask) {
25987cc92483SSepherosa Ziehau 			if (bootverbose) {
2599af85d4d5SSepherosa Ziehau 				if_printf(sc->ifp, "%s:%s\n", cage_type,
26008892ea20SAggelos Economopoulos 				    mxge_media_types[i].name);
26017cc92483SSepherosa Ziehau 			}
26028892ea20SAggelos Economopoulos 
260389d55360SSepherosa Ziehau 			if (sc->current_media != mxge_media_types[i].flag) {
2604166c46afSSepherosa Ziehau 				mxge_media_unset(sc);
260589d55360SSepherosa Ziehau 				mxge_media_set(sc, mxge_media_types[i].flag);
260689d55360SSepherosa Ziehau 			}
26078892ea20SAggelos Economopoulos 			return;
26088892ea20SAggelos Economopoulos 		}
26098892ea20SAggelos Economopoulos 	}
2610166c46afSSepherosa Ziehau 	mxge_media_unset(sc);
26117cc92483SSepherosa Ziehau 	if (bootverbose) {
2612af85d4d5SSepherosa Ziehau 		if_printf(sc->ifp, "%s media 0x%x unknown\n", cage_type,
26137cc92483SSepherosa Ziehau 		    cmd.data0);
26147cc92483SSepherosa Ziehau 	}
26158892ea20SAggelos Economopoulos }
26168892ea20SAggelos Economopoulos 
26178892ea20SAggelos Economopoulos static void
mxge_intr_status(struct mxge_softc * sc,const mcp_irq_data_t * stats)2618cf5afd69SSepherosa Ziehau mxge_intr_status(struct mxge_softc *sc, const mcp_irq_data_t *stats)
26198892ea20SAggelos Economopoulos {
26208892ea20SAggelos Economopoulos 	if (sc->link_state != stats->link_up) {
26218892ea20SAggelos Economopoulos 		sc->link_state = stats->link_up;
26228892ea20SAggelos Economopoulos 		if (sc->link_state) {
262373a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_UP;
262473a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26257cc92483SSepherosa Ziehau 			if (bootverbose)
2626cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link up\n");
26278892ea20SAggelos Economopoulos 		} else {
262873a22abeSAggelos Economopoulos 			sc->ifp->if_link_state = LINK_STATE_DOWN;
262973a22abeSAggelos Economopoulos 			if_link_state_change(sc->ifp);
26307cc92483SSepherosa Ziehau 			if (bootverbose)
2631cf5afd69SSepherosa Ziehau 				if_printf(sc->ifp, "link down\n");
26328892ea20SAggelos Economopoulos 		}
26338892ea20SAggelos Economopoulos 		sc->need_media_probe = 1;
26348892ea20SAggelos Economopoulos 	}
2635cf5afd69SSepherosa Ziehau 
2636cf5afd69SSepherosa Ziehau 	if (sc->rdma_tags_available != be32toh(stats->rdma_tags_available)) {
2637cf5afd69SSepherosa Ziehau 		sc->rdma_tags_available = be32toh(stats->rdma_tags_available);
2638cf5afd69SSepherosa Ziehau 		if_printf(sc->ifp, "RDMA timed out! %d tags left\n",
2639cf5afd69SSepherosa Ziehau 		    sc->rdma_tags_available);
26408892ea20SAggelos Economopoulos 	}
26418892ea20SAggelos Economopoulos 
26428892ea20SAggelos Economopoulos 	if (stats->link_down) {
26438892ea20SAggelos Economopoulos 		sc->down_cnt += stats->link_down;
26448892ea20SAggelos Economopoulos 		sc->link_state = 0;
2645f0115d64SAggelos Economopoulos 		sc->ifp->if_link_state = LINK_STATE_DOWN;
2646f0115d64SAggelos Economopoulos 		if_link_state_change(sc->ifp);
26478892ea20SAggelos Economopoulos 	}
26488892ea20SAggelos Economopoulos }
26498892ea20SAggelos Economopoulos 
2650cf5afd69SSepherosa Ziehau static void
mxge_serialize_skipmain(struct mxge_softc * sc)265126634ef8SSepherosa Ziehau mxge_serialize_skipmain(struct mxge_softc *sc)
265226634ef8SSepherosa Ziehau {
265326634ef8SSepherosa Ziehau 	lwkt_serialize_array_enter(sc->serializes, sc->nserialize, 1);
265426634ef8SSepherosa Ziehau }
265526634ef8SSepherosa Ziehau 
265626634ef8SSepherosa Ziehau static void
mxge_deserialize_skipmain(struct mxge_softc * sc)265726634ef8SSepherosa Ziehau mxge_deserialize_skipmain(struct mxge_softc *sc)
265826634ef8SSepherosa Ziehau {
265926634ef8SSepherosa Ziehau 	lwkt_serialize_array_exit(sc->serializes, sc->nserialize, 1);
266026634ef8SSepherosa Ziehau }
266126634ef8SSepherosa Ziehau 
266226634ef8SSepherosa Ziehau static void
mxge_legacy(void * arg)2663cf5afd69SSepherosa Ziehau mxge_legacy(void *arg)
2664cf5afd69SSepherosa Ziehau {
2665cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2666cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2667cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2668cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
26699a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2670cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2671cf5afd69SSepherosa Ziehau 	uint8_t valid;
2672cf5afd69SSepherosa Ziehau 
267326634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
267426634ef8SSepherosa Ziehau 
2675cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
2676cf5afd69SSepherosa Ziehau 	if (!stats->valid)
2677cf5afd69SSepherosa Ziehau 		return;
2678cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2679cf5afd69SSepherosa Ziehau 
2680cf5afd69SSepherosa Ziehau 	/* Lower legacy IRQ */
2681cf5afd69SSepherosa Ziehau 	*sc->irq_deassert = 0;
2682cf5afd69SSepherosa Ziehau 	if (!mxge_deassert_wait) {
2683cf5afd69SSepherosa Ziehau 		/* Don't wait for conf. that irq is low */
2684cf5afd69SSepherosa Ziehau 		stats->valid = 0;
2685cf5afd69SSepherosa Ziehau 	}
2686cf5afd69SSepherosa Ziehau 
268726634ef8SSepherosa Ziehau 	mxge_serialize_skipmain(sc);
268826634ef8SSepherosa Ziehau 
2689cf5afd69SSepherosa Ziehau 	/*
2690cf5afd69SSepherosa Ziehau 	 * Loop while waiting for legacy irq deassertion
2691cf5afd69SSepherosa Ziehau 	 * XXX do we really want to loop?
2692cf5afd69SSepherosa Ziehau 	 */
2693cf5afd69SSepherosa Ziehau 	do {
2694cf5afd69SSepherosa Ziehau 		/* Check for transmit completes and receives */
2695cf5afd69SSepherosa Ziehau 		send_done_count = be32toh(stats->send_done_count);
2696cf5afd69SSepherosa Ziehau 		while ((send_done_count != tx->pkt_done) ||
2697cf5afd69SSepherosa Ziehau 		       (rx_done->entry[rx_done->idx].length != 0)) {
26985da1e9c3SSepherosa Ziehau 			if (send_done_count != tx->pkt_done) {
26995da1e9c3SSepherosa Ziehau 				mxge_tx_done(&sc->arpcom.ac_if, tx,
27005da1e9c3SSepherosa Ziehau 				    (int)send_done_count);
27015da1e9c3SSepherosa Ziehau 			}
27022276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2703cf5afd69SSepherosa Ziehau 			send_done_count = be32toh(stats->send_done_count);
2704cf5afd69SSepherosa Ziehau 		}
2705cf5afd69SSepherosa Ziehau 		if (mxge_deassert_wait)
2706cf5afd69SSepherosa Ziehau 			wmb();
2707cf5afd69SSepherosa Ziehau 	} while (*((volatile uint8_t *)&stats->valid));
2708cf5afd69SSepherosa Ziehau 
270926634ef8SSepherosa Ziehau 	mxge_deserialize_skipmain(sc);
271026634ef8SSepherosa Ziehau 
2711cf5afd69SSepherosa Ziehau 	/* Fw link & error stats meaningful only on the first slice */
2712cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2713cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2714cf5afd69SSepherosa Ziehau 
2715cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
2716cf5afd69SSepherosa Ziehau 	if (valid & 0x1)
2717cf5afd69SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2718cf5afd69SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2719cf5afd69SSepherosa Ziehau }
2720cf5afd69SSepherosa Ziehau 
2721cf5afd69SSepherosa Ziehau static void
mxge_msi(void * arg)2722cf5afd69SSepherosa Ziehau mxge_msi(void *arg)
2723cf5afd69SSepherosa Ziehau {
2724cf5afd69SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2725cf5afd69SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2726cf5afd69SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2727cf5afd69SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
27289a4ae890SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2729cf5afd69SSepherosa Ziehau 	uint32_t send_done_count;
2730cf5afd69SSepherosa Ziehau 	uint8_t valid;
27312276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
27322276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
27332276707eSSepherosa Ziehau #else
27342276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
27352276707eSSepherosa Ziehau #endif
2736cf5afd69SSepherosa Ziehau 
273726634ef8SSepherosa Ziehau 	ASSERT_SERIALIZED(&sc->main_serialize);
273826634ef8SSepherosa Ziehau 
2739cf5afd69SSepherosa Ziehau 	/* Make sure the DMA has finished */
274076b52164SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2741cf5afd69SSepherosa Ziehau 		return;
2742cf5afd69SSepherosa Ziehau 
2743cf5afd69SSepherosa Ziehau 	valid = stats->valid;
2744cf5afd69SSepherosa Ziehau 	stats->valid = 0;
2745cf5afd69SSepherosa Ziehau 
27462276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27472276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27482276707eSSepherosa Ziehau 		polling = TRUE;
27492276707eSSepherosa Ziehau #endif
27502276707eSSepherosa Ziehau 
27512276707eSSepherosa Ziehau 	if (!polling) {
2752cf5afd69SSepherosa Ziehau 		/* Check for receives */
275326634ef8SSepherosa Ziehau 		lwkt_serialize_enter(&ss->rx_data.rx_serialize);
2754cf5afd69SSepherosa Ziehau 		if (rx_done->entry[rx_done->idx].length != 0)
27552276707eSSepherosa Ziehau 			mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
275626634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&ss->rx_data.rx_serialize);
27572276707eSSepherosa Ziehau 	}
2758cf5afd69SSepherosa Ziehau 
275945bc9b9dSSepherosa Ziehau 	/*
276045bc9b9dSSepherosa Ziehau 	 * Check for transmit completes
276145bc9b9dSSepherosa Ziehau 	 *
276245bc9b9dSSepherosa Ziehau 	 * NOTE:
276345bc9b9dSSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
276445bc9b9dSSepherosa Ziehau 	 * which is called only in interrupt handler, the
276545bc9b9dSSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
276645bc9b9dSSepherosa Ziehau 	 */
2767cf5afd69SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
276845bc9b9dSSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
276945bc9b9dSSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
27705da1e9c3SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
277126634ef8SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
277245bc9b9dSSepherosa Ziehau 	}
2773cf5afd69SSepherosa Ziehau 
2774cf5afd69SSepherosa Ziehau 	if (__predict_false(stats->stats_updated))
2775cf5afd69SSepherosa Ziehau 		mxge_intr_status(sc, stats);
2776cf5afd69SSepherosa Ziehau 
2777cf5afd69SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
27782276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
27798892ea20SAggelos Economopoulos 		*ss->irq_claim = be32toh(3);
27808892ea20SAggelos Economopoulos 	*(ss->irq_claim + 1) = be32toh(3);
27818892ea20SAggelos Economopoulos }
27828892ea20SAggelos Economopoulos 
27838892ea20SAggelos Economopoulos static void
mxge_msix_rx(void * arg)2784e6c7b753SSepherosa Ziehau mxge_msix_rx(void *arg)
2785e6c7b753SSepherosa Ziehau {
2786e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2787e6c7b753SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2788e6c7b753SSepherosa Ziehau 
27892276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
27902276707eSSepherosa Ziehau 	if (ss->sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
27912276707eSSepherosa Ziehau 		return;
27922276707eSSepherosa Ziehau #endif
27932276707eSSepherosa Ziehau 
2794e6c7b753SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2795e6c7b753SSepherosa Ziehau 
2796e6c7b753SSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0)
27972276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, -1);
2798e6c7b753SSepherosa Ziehau 
2799e6c7b753SSepherosa Ziehau 	*ss->irq_claim = be32toh(3);
2800e6c7b753SSepherosa Ziehau }
2801e6c7b753SSepherosa Ziehau 
2802e6c7b753SSepherosa Ziehau static void
mxge_msix_rxtx(void * arg)2803aca8f373SSepherosa Ziehau mxge_msix_rxtx(void *arg)
2804aca8f373SSepherosa Ziehau {
2805aca8f373SSepherosa Ziehau 	struct mxge_slice_state *ss = arg;
2806aca8f373SSepherosa Ziehau 	mxge_softc_t *sc = ss->sc;
2807aca8f373SSepherosa Ziehau 	mcp_irq_data_t *stats = ss->fw_stats;
2808aca8f373SSepherosa Ziehau 	mxge_tx_ring_t *tx = &ss->tx;
2809aca8f373SSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
2810aca8f373SSepherosa Ziehau 	uint32_t send_done_count;
2811aca8f373SSepherosa Ziehau 	uint8_t valid;
28122276707eSSepherosa Ziehau #ifndef IFPOLL_ENABLE
28132276707eSSepherosa Ziehau 	const boolean_t polling = FALSE;
28142276707eSSepherosa Ziehau #else
28152276707eSSepherosa Ziehau 	boolean_t polling = FALSE;
28162276707eSSepherosa Ziehau #endif
2817aca8f373SSepherosa Ziehau 
2818aca8f373SSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
2819aca8f373SSepherosa Ziehau 
2820aca8f373SSepherosa Ziehau 	/* Make sure the DMA has finished */
2821aca8f373SSepherosa Ziehau 	if (__predict_false(!stats->valid))
2822aca8f373SSepherosa Ziehau 		return;
2823aca8f373SSepherosa Ziehau 
2824aca8f373SSepherosa Ziehau 	valid = stats->valid;
2825aca8f373SSepherosa Ziehau 	stats->valid = 0;
2826aca8f373SSepherosa Ziehau 
28272276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
28282276707eSSepherosa Ziehau 	if (sc->arpcom.ac_if.if_flags & IFF_NPOLLING)
28292276707eSSepherosa Ziehau 		polling = TRUE;
28302276707eSSepherosa Ziehau #endif
28312276707eSSepherosa Ziehau 
2832aca8f373SSepherosa Ziehau 	/* Check for receives */
28332276707eSSepherosa Ziehau 	if (!polling && rx_done->entry[rx_done->idx].length != 0)
28342276707eSSepherosa Ziehau 		mxge_clean_rx_done(&sc->arpcom.ac_if, &ss->rx_data, -1);
2835aca8f373SSepherosa Ziehau 
2836aca8f373SSepherosa Ziehau 	/*
2837aca8f373SSepherosa Ziehau 	 * Check for transmit completes
2838aca8f373SSepherosa Ziehau 	 *
2839aca8f373SSepherosa Ziehau 	 * NOTE:
2840aca8f373SSepherosa Ziehau 	 * Since pkt_done is only changed by mxge_tx_done(),
2841aca8f373SSepherosa Ziehau 	 * which is called only in interrupt handler, the
2842aca8f373SSepherosa Ziehau 	 * check w/o holding tx serializer is MPSAFE.
2843aca8f373SSepherosa Ziehau 	 */
2844aca8f373SSepherosa Ziehau 	send_done_count = be32toh(stats->send_done_count);
2845aca8f373SSepherosa Ziehau 	if (send_done_count != tx->pkt_done) {
2846aca8f373SSepherosa Ziehau 		lwkt_serialize_enter(&tx->tx_serialize);
2847aca8f373SSepherosa Ziehau 		mxge_tx_done(&sc->arpcom.ac_if, tx, (int)send_done_count);
2848aca8f373SSepherosa Ziehau 		lwkt_serialize_exit(&tx->tx_serialize);
2849aca8f373SSepherosa Ziehau 	}
2850aca8f373SSepherosa Ziehau 
2851aca8f373SSepherosa Ziehau 	/* Check to see if we have rx token to pass back */
28522276707eSSepherosa Ziehau 	if (!polling && (valid & 0x1))
2853aca8f373SSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
2854aca8f373SSepherosa Ziehau 	*(ss->irq_claim + 1) = be32toh(3);
2855aca8f373SSepherosa Ziehau }
2856aca8f373SSepherosa Ziehau 
2857aca8f373SSepherosa Ziehau static void
mxge_init(void * arg)28588892ea20SAggelos Economopoulos mxge_init(void *arg)
28598892ea20SAggelos Economopoulos {
286089d55360SSepherosa Ziehau 	struct mxge_softc *sc = arg;
286189d55360SSepherosa Ziehau 
286226634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(sc->ifp);
286389d55360SSepherosa Ziehau 	if ((sc->ifp->if_flags & IFF_RUNNING) == 0)
286489d55360SSepherosa Ziehau 		mxge_open(sc);
28658892ea20SAggelos Economopoulos }
28668892ea20SAggelos Economopoulos 
28678892ea20SAggelos Economopoulos static void
mxge_free_slice_mbufs(struct mxge_slice_state * ss)28688892ea20SAggelos Economopoulos mxge_free_slice_mbufs(struct mxge_slice_state *ss)
28698892ea20SAggelos Economopoulos {
28708892ea20SAggelos Economopoulos 	int i;
28718892ea20SAggelos Economopoulos 
28729a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
28739a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.info[i].m == NULL)
28748892ea20SAggelos Economopoulos 			continue;
28759a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_big.dmat,
28769a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map);
28779a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_big.info[i].m);
28789a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info[i].m = NULL;
28798892ea20SAggelos Economopoulos 	}
28808892ea20SAggelos Economopoulos 
28819a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
28829a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.info[i].m == NULL)
28838892ea20SAggelos Economopoulos 			continue;
28849a4ae890SSepherosa Ziehau 		bus_dmamap_unload(ss->rx_data.rx_small.dmat,
28859a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map);
28869a4ae890SSepherosa Ziehau 		m_freem(ss->rx_data.rx_small.info[i].m);
28879a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info[i].m = NULL;
28888892ea20SAggelos Economopoulos 	}
28898892ea20SAggelos Economopoulos 
28904e5bf8bdSSepherosa Ziehau 	/* Transmit ring used only on the first slice */
28918892ea20SAggelos Economopoulos 	if (ss->tx.info == NULL)
28928892ea20SAggelos Economopoulos 		return;
28938892ea20SAggelos Economopoulos 
28948892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
28958892ea20SAggelos Economopoulos 		if (ss->tx.info[i].m == NULL)
28968892ea20SAggelos Economopoulos 			continue;
28974e5bf8bdSSepherosa Ziehau 		bus_dmamap_unload(ss->tx.dmat, ss->tx.info[i].map);
28988892ea20SAggelos Economopoulos 		m_freem(ss->tx.info[i].m);
28998892ea20SAggelos Economopoulos 		ss->tx.info[i].m = NULL;
29008892ea20SAggelos Economopoulos 	}
29018892ea20SAggelos Economopoulos }
29028892ea20SAggelos Economopoulos 
29038892ea20SAggelos Economopoulos static void
mxge_free_mbufs(mxge_softc_t * sc)29048892ea20SAggelos Economopoulos mxge_free_mbufs(mxge_softc_t *sc)
29058892ea20SAggelos Economopoulos {
29068892ea20SAggelos Economopoulos 	int slice;
29078892ea20SAggelos Economopoulos 
29088892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29098892ea20SAggelos Economopoulos 		mxge_free_slice_mbufs(&sc->ss[slice]);
29108892ea20SAggelos Economopoulos }
29118892ea20SAggelos Economopoulos 
29128892ea20SAggelos Economopoulos static void
mxge_free_slice_rings(struct mxge_slice_state * ss)29138892ea20SAggelos Economopoulos mxge_free_slice_rings(struct mxge_slice_state *ss)
29148892ea20SAggelos Economopoulos {
29158892ea20SAggelos Economopoulos 	int i;
29168892ea20SAggelos Economopoulos 
29179a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_done.entry != NULL) {
2918414caf0dSSepherosa Ziehau 		mxge_dma_free(&ss->rx_done_dma);
29199a4ae890SSepherosa Ziehau 		ss->rx_data.rx_done.entry = NULL;
2920798c3369SSepherosa Ziehau 	}
29218892ea20SAggelos Economopoulos 
292211868a93SSepherosa Ziehau 	if (ss->tx.req_list != NULL) {
292311868a93SSepherosa Ziehau 		kfree(ss->tx.req_list, M_DEVBUF);
292411868a93SSepherosa Ziehau 		ss->tx.req_list = NULL;
2925798c3369SSepherosa Ziehau 	}
29268892ea20SAggelos Economopoulos 
2927798c3369SSepherosa Ziehau 	if (ss->tx.seg_list != NULL) {
2928d777b84fSAggelos Economopoulos 		kfree(ss->tx.seg_list, M_DEVBUF);
29298892ea20SAggelos Economopoulos 		ss->tx.seg_list = NULL;
2930798c3369SSepherosa Ziehau 	}
29318892ea20SAggelos Economopoulos 
29329a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.shadow != NULL) {
29339a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.shadow, M_DEVBUF);
29349a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.shadow = NULL;
2935798c3369SSepherosa Ziehau 	}
29368892ea20SAggelos Economopoulos 
29379a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.shadow != NULL) {
29389a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.shadow, M_DEVBUF);
29399a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow = NULL;
2940798c3369SSepherosa Ziehau 	}
29418892ea20SAggelos Economopoulos 
29428892ea20SAggelos Economopoulos 	if (ss->tx.info != NULL) {
29438892ea20SAggelos Economopoulos 		if (ss->tx.dmat != NULL) {
29448892ea20SAggelos Economopoulos 			for (i = 0; i <= ss->tx.mask; i++) {
29458892ea20SAggelos Economopoulos 				bus_dmamap_destroy(ss->tx.dmat,
29468892ea20SAggelos Economopoulos 				    ss->tx.info[i].map);
29478892ea20SAggelos Economopoulos 			}
29488892ea20SAggelos Economopoulos 			bus_dma_tag_destroy(ss->tx.dmat);
29498892ea20SAggelos Economopoulos 		}
2950d777b84fSAggelos Economopoulos 		kfree(ss->tx.info, M_DEVBUF);
29518892ea20SAggelos Economopoulos 		ss->tx.info = NULL;
2952798c3369SSepherosa Ziehau 	}
29538892ea20SAggelos Economopoulos 
29549a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_small.info != NULL) {
29559a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_small.dmat != NULL) {
29569a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
29579a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29589a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_small.info[i].map);
29598892ea20SAggelos Economopoulos 			}
29609a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_small.dmat,
29619a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.extra_map);
29629a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_small.dmat);
29638892ea20SAggelos Economopoulos 		}
29649a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_small.info, M_DEVBUF);
29659a4ae890SSepherosa Ziehau 		ss->rx_data.rx_small.info = NULL;
2966798c3369SSepherosa Ziehau 	}
29678892ea20SAggelos Economopoulos 
29689a4ae890SSepherosa Ziehau 	if (ss->rx_data.rx_big.info != NULL) {
29699a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_big.dmat != NULL) {
29709a4ae890SSepherosa Ziehau 			for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
29719a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29729a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[i].map);
29738892ea20SAggelos Economopoulos 			}
29749a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
29759a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
29769a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
29778892ea20SAggelos Economopoulos 		}
29789a4ae890SSepherosa Ziehau 		kfree(ss->rx_data.rx_big.info, M_DEVBUF);
29799a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.info = NULL;
29808892ea20SAggelos Economopoulos 	}
2981798c3369SSepherosa Ziehau }
29828892ea20SAggelos Economopoulos 
29838892ea20SAggelos Economopoulos static void
mxge_free_rings(mxge_softc_t * sc)29848892ea20SAggelos Economopoulos mxge_free_rings(mxge_softc_t *sc)
29858892ea20SAggelos Economopoulos {
29868892ea20SAggelos Economopoulos 	int slice;
29878892ea20SAggelos Economopoulos 
2988798c3369SSepherosa Ziehau 	if (sc->ss == NULL)
2989798c3369SSepherosa Ziehau 		return;
2990798c3369SSepherosa Ziehau 
29918892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++)
29928892ea20SAggelos Economopoulos 		mxge_free_slice_rings(&sc->ss[slice]);
29938892ea20SAggelos Economopoulos }
29948892ea20SAggelos Economopoulos 
29958892ea20SAggelos Economopoulos static int
mxge_alloc_slice_rings(struct mxge_slice_state * ss,int rx_ring_entries,int tx_ring_entries)29968892ea20SAggelos Economopoulos mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries,
29978892ea20SAggelos Economopoulos     int tx_ring_entries)
29988892ea20SAggelos Economopoulos {
29998892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ss->sc;
30008892ea20SAggelos Economopoulos 	size_t bytes;
30018892ea20SAggelos Economopoulos 	int err, i;
30028892ea20SAggelos Economopoulos 
30037cc92483SSepherosa Ziehau 	/*
30047cc92483SSepherosa Ziehau 	 * Allocate per-slice receive resources
30057cc92483SSepherosa Ziehau 	 */
30068892ea20SAggelos Economopoulos 
30079a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.mask = ss->rx_data.rx_big.mask =
30089a4ae890SSepherosa Ziehau 	    rx_ring_entries - 1;
30099a4ae890SSepherosa Ziehau 	ss->rx_data.rx_done.mask = (2 * rx_ring_entries) - 1;
30108892ea20SAggelos Economopoulos 
30117cc92483SSepherosa Ziehau 	/* Allocate the rx shadow rings */
30129a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.shadow);
30139a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30148892ea20SAggelos Economopoulos 
30159a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.shadow);
30169a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.shadow = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30178892ea20SAggelos Economopoulos 
30187cc92483SSepherosa Ziehau 	/* Allocate the rx host info rings */
30199a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_small.info);
30209a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30218892ea20SAggelos Economopoulos 
30229a4ae890SSepherosa Ziehau 	bytes = rx_ring_entries * sizeof(*ss->rx_data.rx_big.info);
30239a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.info = kmalloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK);
30248892ea20SAggelos Economopoulos 
30257cc92483SSepherosa Ziehau 	/* Allocate the rx busdma resources */
30268892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(sc->parent_dmat,	/* parent */
30278892ea20SAggelos Economopoulos 				 1,			/* alignment */
30288892ea20SAggelos Economopoulos 				 4096,			/* boundary */
30298892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
30308892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
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 */
3076b9a8961fSSepherosa Ziehau 				 4096,			/* maxsize */
3077b9a8961fSSepherosa Ziehau 				 1,			/* num segs */
30788892ea20SAggelos Economopoulos 				 4096,			/* maxsegsize*/
30797cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
30807cc92483SSepherosa Ziehau 				 			/* flags */
30819a4ae890SSepherosa Ziehau 				 &ss->rx_data.rx_big.dmat); /* tag */
30828892ea20SAggelos Economopoulos 	if (err != 0) {
30838892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Err %d allocating rx_big dmat\n",
30848892ea20SAggelos Economopoulos 		    err);
30853598cc14SSascha Wildner 		return err;
30868892ea20SAggelos Economopoulos 	}
30877cc92483SSepherosa Ziehau 
30889a4ae890SSepherosa Ziehau 	err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30899a4ae890SSepherosa Ziehau 	    &ss->rx_data.rx_big.extra_map);
30908892ea20SAggelos Economopoulos 	if (err != 0) {
30917cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d extra rx_big dmamap\n", err);
30929a4ae890SSepherosa Ziehau 		bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
30939a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.dmat = NULL;
30943598cc14SSascha Wildner 		return err;
30958892ea20SAggelos Economopoulos 	}
30969a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
30979a4ae890SSepherosa Ziehau 		err = bus_dmamap_create(ss->rx_data.rx_big.dmat, BUS_DMA_WAITOK,
30989a4ae890SSepherosa Ziehau 		    &ss->rx_data.rx_big.info[i].map);
3099798c3369SSepherosa Ziehau 		if (err != 0) {
3100798c3369SSepherosa Ziehau 			int j;
3101798c3369SSepherosa Ziehau 
3102798c3369SSepherosa Ziehau 			device_printf(sc->dev, "Err %d rx_big dmamap\n", err);
3103798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
31049a4ae890SSepherosa Ziehau 				bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31059a4ae890SSepherosa Ziehau 				    ss->rx_data.rx_big.info[j].map);
3106798c3369SSepherosa Ziehau 			}
31079a4ae890SSepherosa Ziehau 			bus_dmamap_destroy(ss->rx_data.rx_big.dmat,
31089a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.extra_map);
31099a4ae890SSepherosa Ziehau 			bus_dma_tag_destroy(ss->rx_data.rx_big.dmat);
31109a4ae890SSepherosa Ziehau 			ss->rx_data.rx_big.dmat = NULL;
3111798c3369SSepherosa Ziehau 			return err;
3112798c3369SSepherosa Ziehau 		}
3113798c3369SSepherosa Ziehau 	}
31148892ea20SAggelos Economopoulos 
31157cc92483SSepherosa Ziehau 	/*
31167cc92483SSepherosa Ziehau 	 * Now allocate TX resources
31177cc92483SSepherosa Ziehau 	 */
31188892ea20SAggelos Economopoulos 
31198892ea20SAggelos Economopoulos 	ss->tx.mask = tx_ring_entries - 1;
31208892ea20SAggelos Economopoulos 	ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4);
31218892ea20SAggelos Economopoulos 
3122dfda108aSSepherosa Ziehau 	/*
3123dfda108aSSepherosa Ziehau 	 * Allocate the tx request copy block; MUST be at least 8 bytes
3124dfda108aSSepherosa Ziehau 	 * aligned
3125dfda108aSSepherosa Ziehau 	 */
312611868a93SSepherosa Ziehau 	bytes = sizeof(*ss->tx.req_list) * (ss->tx.max_desc + 4);
312762938642SMatthew Dillon 	ss->tx.req_list = kmalloc(__VM_CACHELINE_ALIGN(bytes),
312862938642SMatthew Dillon 				  M_DEVBUF,
312962938642SMatthew Dillon 				  M_WAITOK | M_CACHEALIGN);
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 */
31457cc92483SSepherosa Ziehau 				 IP_MAXPACKET +
31467cc92483SSepherosa Ziehau 				 sizeof(struct ether_vlan_header),
31477cc92483SSepherosa Ziehau 				 			/* maxsize */
31488892ea20SAggelos Economopoulos 				 ss->tx.max_desc - 2,	/* num segs */
31498892ea20SAggelos Economopoulos 				 sc->tx_boundary,	/* maxsegsz */
31507cc92483SSepherosa Ziehau 				 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW |
31517cc92483SSepherosa Ziehau 				 BUS_DMA_ONEBPAGE,	/* flags */
31528892ea20SAggelos Economopoulos 				 &ss->tx.dmat);		/* tag */
31538892ea20SAggelos Economopoulos 	if (err != 0) {
31547cc92483SSepherosa Ziehau 		device_printf(sc->dev, "Err %d allocating tx dmat\n", err);
31553598cc14SSascha Wildner 		return err;
31568892ea20SAggelos Economopoulos 	}
31578892ea20SAggelos Economopoulos 
31587cc92483SSepherosa Ziehau 	/*
31597cc92483SSepherosa Ziehau 	 * Now use these tags to setup DMA maps for each slot in the ring
31607cc92483SSepherosa Ziehau 	 */
31618892ea20SAggelos Economopoulos 	for (i = 0; i <= ss->tx.mask; i++) {
31627cc92483SSepherosa Ziehau 		err = bus_dmamap_create(ss->tx.dmat,
31637cc92483SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &ss->tx.info[i].map);
31648892ea20SAggelos Economopoulos 		if (err != 0) {
3165798c3369SSepherosa Ziehau 			int j;
3166798c3369SSepherosa Ziehau 
31677cc92483SSepherosa Ziehau 			device_printf(sc->dev, "Err %d tx dmamap\n", err);
3168798c3369SSepherosa Ziehau 			for (j = 0; j < i; ++j) {
3169798c3369SSepherosa Ziehau 				bus_dmamap_destroy(ss->tx.dmat,
3170798c3369SSepherosa Ziehau 				    ss->tx.info[j].map);
3171798c3369SSepherosa Ziehau 			}
3172798c3369SSepherosa Ziehau 			bus_dma_tag_destroy(ss->tx.dmat);
3173798c3369SSepherosa Ziehau 			ss->tx.dmat = NULL;
31743598cc14SSascha Wildner 			return err;
31758892ea20SAggelos Economopoulos 		}
31768892ea20SAggelos Economopoulos 	}
31778892ea20SAggelos Economopoulos 	return 0;
31788892ea20SAggelos Economopoulos }
31798892ea20SAggelos Economopoulos 
31808892ea20SAggelos Economopoulos static int
mxge_alloc_rings(mxge_softc_t * sc)31818892ea20SAggelos Economopoulos mxge_alloc_rings(mxge_softc_t *sc)
31828892ea20SAggelos Economopoulos {
31838892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
31848892ea20SAggelos Economopoulos 	int tx_ring_size;
31858892ea20SAggelos Economopoulos 	int tx_ring_entries, rx_ring_entries;
31868892ea20SAggelos Economopoulos 	int err, slice;
31878892ea20SAggelos Economopoulos 
31887cc92483SSepherosa Ziehau 	/* Get ring sizes */
31898892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd);
31908892ea20SAggelos Economopoulos 	if (err != 0) {
31918892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine tx ring sizes\n");
3192798c3369SSepherosa Ziehau 		return err;
31938892ea20SAggelos Economopoulos 	}
31947cc92483SSepherosa Ziehau 	tx_ring_size = cmd.data0;
31958892ea20SAggelos Economopoulos 
31968892ea20SAggelos Economopoulos 	tx_ring_entries = tx_ring_size / sizeof(mcp_kreq_ether_send_t);
3197089301c2SSepherosa Ziehau 	rx_ring_entries = sc->rx_intr_slots / 2;
3198aca8f373SSepherosa Ziehau 
319918f86fd8SSepherosa Ziehau 	if (bootverbose) {
320018f86fd8SSepherosa Ziehau 		device_printf(sc->dev, "tx desc %d, rx desc %d\n",
320118f86fd8SSepherosa Ziehau 		    tx_ring_entries, rx_ring_entries);
320218f86fd8SSepherosa Ziehau 	}
320318f86fd8SSepherosa Ziehau 
320414929979SSepherosa Ziehau 	sc->ifp->if_nmbclusters = rx_ring_entries * sc->num_slices;
320514929979SSepherosa Ziehau 	sc->ifp->if_nmbjclusters = sc->ifp->if_nmbclusters;
320614929979SSepherosa Ziehau 
3207f2f758dfSAggelos Economopoulos 	ifq_set_maxlen(&sc->ifp->if_snd, tx_ring_entries - 1);
3208f2f758dfSAggelos Economopoulos 	ifq_set_ready(&sc->ifp->if_snd);
3209aca8f373SSepherosa Ziehau 	ifq_set_subq_cnt(&sc->ifp->if_snd, sc->num_tx_rings);
3210aca8f373SSepherosa Ziehau 
3211aca8f373SSepherosa Ziehau 	if (sc->num_tx_rings > 1) {
3212bdbc20adSSepherosa Ziehau 		sc->ifp->if_mapsubq = ifq_mapsubq_modulo;
3213bdbc20adSSepherosa Ziehau 		ifq_set_subq_divisor(&sc->ifp->if_snd, sc->num_tx_rings);
3214aca8f373SSepherosa Ziehau 	}
32158892ea20SAggelos Economopoulos 
32168892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
32178892ea20SAggelos Economopoulos 		err = mxge_alloc_slice_rings(&sc->ss[slice],
32187cc92483SSepherosa Ziehau 		    rx_ring_entries, tx_ring_entries);
3219798c3369SSepherosa Ziehau 		if (err != 0) {
3220798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3221798c3369SSepherosa Ziehau 			    "alloc %d slice rings failed\n", slice);
3222798c3369SSepherosa Ziehau 			return err;
3223798c3369SSepherosa Ziehau 		}
32248892ea20SAggelos Economopoulos 	}
32258892ea20SAggelos Economopoulos 	return 0;
32268892ea20SAggelos Economopoulos }
32278892ea20SAggelos Economopoulos 
32288892ea20SAggelos Economopoulos static void
mxge_choose_params(int mtu,int * cl_size)3229b9a8961fSSepherosa Ziehau mxge_choose_params(int mtu, int *cl_size)
32308892ea20SAggelos Economopoulos {
3231b915556eSAggelos Economopoulos 	int bufsize = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + MXGEFW_PAD;
32328892ea20SAggelos Economopoulos 
32338892ea20SAggelos Economopoulos 	if (bufsize < MCLBYTES) {
32348892ea20SAggelos Economopoulos 		*cl_size = MCLBYTES;
3235b9a8961fSSepherosa Ziehau 	} else {
3236b9a8961fSSepherosa Ziehau 		KASSERT(bufsize < MJUMPAGESIZE, ("invalid MTU %d", mtu));
32378892ea20SAggelos Economopoulos 		*cl_size = MJUMPAGESIZE;
32388892ea20SAggelos Economopoulos 	}
32398892ea20SAggelos Economopoulos }
32408892ea20SAggelos Economopoulos 
32418892ea20SAggelos Economopoulos static int
mxge_slice_open(struct mxge_slice_state * ss,int cl_size)3242b9a8961fSSepherosa Ziehau mxge_slice_open(struct mxge_slice_state *ss, int cl_size)
32438892ea20SAggelos Economopoulos {
32448892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
32458892ea20SAggelos Economopoulos 	int err, i, slice;
32468892ea20SAggelos Economopoulos 
3247308781adSSepherosa Ziehau 	slice = ss - ss->sc->ss;
32488892ea20SAggelos Economopoulos 
3249308781adSSepherosa Ziehau 	/*
3250308781adSSepherosa Ziehau 	 * Get the lanai pointers to the send and receive rings
3251308781adSSepherosa Ziehau 	 */
32528892ea20SAggelos Economopoulos 	err = 0;
3253aca8f373SSepherosa Ziehau 
325457e09377SMatthew Dillon 	bzero(&cmd, sizeof(cmd));	/* silence gcc warning */
3255aca8f373SSepherosa Ziehau 	if (ss->sc->num_tx_rings == 1) {
3256aca8f373SSepherosa Ziehau 		if (slice == 0) {
3257aca8f373SSepherosa Ziehau 			cmd.data0 = slice;
3258aca8f373SSepherosa Ziehau 			err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET,
3259aca8f373SSepherosa Ziehau 			    &cmd);
3260aca8f373SSepherosa Ziehau 			ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3261aca8f373SSepherosa Ziehau 			    (ss->sc->sram + cmd.data0);
3262aca8f373SSepherosa Ziehau 			/* Leave send_go and send_stop as NULL */
3263aca8f373SSepherosa Ziehau 		}
3264aca8f373SSepherosa Ziehau 	} else {
3265aca8f373SSepherosa Ziehau 		cmd.data0 = slice;
3266aca8f373SSepherosa Ziehau 		err = mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd);
3267aca8f373SSepherosa Ziehau 		ss->tx.lanai = (volatile mcp_kreq_ether_send_t *)
3268aca8f373SSepherosa Ziehau 		    (ss->sc->sram + cmd.data0);
3269aca8f373SSepherosa Ziehau 		ss->tx.send_go = (volatile uint32_t *)
3270aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
3271aca8f373SSepherosa Ziehau 		ss->tx.send_stop = (volatile uint32_t *)
3272aca8f373SSepherosa Ziehau 		    (ss->sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
3273aca8f373SSepherosa Ziehau 	}
3274308781adSSepherosa Ziehau 
32758892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3276308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd);
32779a4ae890SSepherosa Ziehau 	ss->rx_data.rx_small.lanai =
3278308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
3279308781adSSepherosa Ziehau 
32808892ea20SAggelos Economopoulos 	cmd.data0 = slice;
3281308781adSSepherosa Ziehau 	err |= mxge_send_cmd(ss->sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd);
32829a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.lanai =
3283308781adSSepherosa Ziehau 	    (volatile mcp_kreq_ether_recv_t *)(ss->sc->sram + cmd.data0);
32848892ea20SAggelos Economopoulos 
32858892ea20SAggelos Economopoulos 	if (err != 0) {
3286308781adSSepherosa Ziehau 		if_printf(ss->sc->ifp,
32878892ea20SAggelos Economopoulos 		    "failed to get ring sizes or locations\n");
32888892ea20SAggelos Economopoulos 		return EIO;
32898892ea20SAggelos Economopoulos 	}
32908892ea20SAggelos Economopoulos 
3291308781adSSepherosa Ziehau 	/*
3292308781adSSepherosa Ziehau 	 * Stock small receive ring
3293308781adSSepherosa Ziehau 	 */
32949a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_small.mask; i++) {
32959a4ae890SSepherosa Ziehau 		err = mxge_get_buf_small(&ss->rx_data.rx_small,
32969a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_small.info[i].map, i, TRUE);
32978892ea20SAggelos Economopoulos 		if (err) {
3298308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d smalls\n", i,
32999a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_small.mask + 1);
33008892ea20SAggelos Economopoulos 			return ENOMEM;
33018892ea20SAggelos Economopoulos 		}
33028892ea20SAggelos Economopoulos 	}
3303308781adSSepherosa Ziehau 
3304308781adSSepherosa Ziehau 	/*
3305308781adSSepherosa Ziehau 	 * Stock big receive ring
3306308781adSSepherosa Ziehau 	 */
33079a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33089a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_low = 0xffffffff;
33099a4ae890SSepherosa Ziehau 		ss->rx_data.rx_big.shadow[i].addr_high = 0xffffffff;
33108892ea20SAggelos Economopoulos 	}
3311308781adSSepherosa Ziehau 
33129a4ae890SSepherosa Ziehau 	ss->rx_data.rx_big.cl_size = cl_size;
3313308781adSSepherosa Ziehau 
33149a4ae890SSepherosa Ziehau 	for (i = 0; i <= ss->rx_data.rx_big.mask; i++) {
33159a4ae890SSepherosa Ziehau 		err = mxge_get_buf_big(&ss->rx_data.rx_big,
33169a4ae890SSepherosa Ziehau 		    ss->rx_data.rx_big.info[i].map, i, TRUE);
33178892ea20SAggelos Economopoulos 		if (err) {
3318308781adSSepherosa Ziehau 			if_printf(ss->sc->ifp, "alloced %d/%d bigs\n", i,
33199a4ae890SSepherosa Ziehau 			    ss->rx_data.rx_big.mask + 1);
33208892ea20SAggelos Economopoulos 			return ENOMEM;
33218892ea20SAggelos Economopoulos 		}
33228892ea20SAggelos Economopoulos 	}
33238892ea20SAggelos Economopoulos 	return 0;
33248892ea20SAggelos Economopoulos }
33258892ea20SAggelos Economopoulos 
33268892ea20SAggelos Economopoulos static int
mxge_open(mxge_softc_t * sc)33278892ea20SAggelos Economopoulos mxge_open(mxge_softc_t *sc)
33288892ea20SAggelos Economopoulos {
33296ee6cba3SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
33308892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3331b9a8961fSSepherosa Ziehau 	int err, slice, cl_size, i;
33328892ea20SAggelos Economopoulos 	bus_addr_t bus;
33338892ea20SAggelos Economopoulos 	volatile uint8_t *itable;
33348892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
33358892ea20SAggelos Economopoulos 
333626634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
33376ee6cba3SSepherosa Ziehau 
33388892ea20SAggelos Economopoulos 	/* Copy the MAC address in case it was overridden */
33396ee6cba3SSepherosa Ziehau 	bcopy(IF_LLADDR(ifp), sc->mac_addr, ETHER_ADDR_LEN);
33408892ea20SAggelos Economopoulos 
33418892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 1);
33428892ea20SAggelos Economopoulos 	if (err != 0) {
33436ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to reset\n");
33448892ea20SAggelos Economopoulos 		return EIO;
33458892ea20SAggelos Economopoulos 	}
33468892ea20SAggelos Economopoulos 
33478892ea20SAggelos Economopoulos 	if (sc->num_slices > 1) {
3348bdbc20adSSepherosa Ziehau 		/*
3349bdbc20adSSepherosa Ziehau 		 * Setup the indirect table.
3350bdbc20adSSepherosa Ziehau 		 */
335130817b13SSepherosa Ziehau 		if_ringmap_rdrtable(sc->ring_map, sc->rdr_table, NETISR_CPUMAX);
3352bdbc20adSSepherosa Ziehau 
3353bdbc20adSSepherosa Ziehau 		cmd.data0 = NETISR_CPUMAX;
335430817b13SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, &cmd);
3355bdbc20adSSepherosa Ziehau 
335630817b13SSepherosa Ziehau 		err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, &cmd);
33578892ea20SAggelos Economopoulos 		if (err != 0) {
33586ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to setup rss tables\n");
33598892ea20SAggelos Economopoulos 			return err;
33608892ea20SAggelos Economopoulos 		}
33618892ea20SAggelos Economopoulos 
33628892ea20SAggelos Economopoulos 		itable = sc->sram + cmd.data0;
3363bdbc20adSSepherosa Ziehau 		for (i = 0; i < NETISR_CPUMAX; i++)
3364bdbc20adSSepherosa Ziehau 			itable[i] = sc->rdr_table[i];
33658892ea20SAggelos Economopoulos 
336630817b13SSepherosa Ziehau 		if (sc->use_rss) {
336730817b13SSepherosa Ziehau 			volatile uint8_t *hwkey;
336830817b13SSepherosa Ziehau 			uint8_t swkey[MXGE_HWRSS_KEYLEN];
336930817b13SSepherosa Ziehau 
3370bdbc20adSSepherosa Ziehau 			/*
3371bdbc20adSSepherosa Ziehau 			 * Setup Toeplitz key.
3372bdbc20adSSepherosa Ziehau 			 */
33731cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_KEY_OFFSET,
33741cd61a7cSSepherosa Ziehau 			    &cmd);
33751cd61a7cSSepherosa Ziehau 			if (err != 0) {
33761cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to get rsskey\n");
33771cd61a7cSSepherosa Ziehau 				return err;
33781cd61a7cSSepherosa Ziehau 			}
33791cd61a7cSSepherosa Ziehau 			hwkey = sc->sram + cmd.data0;
33801cd61a7cSSepherosa Ziehau 
33811cd61a7cSSepherosa Ziehau 			toeplitz_get_key(swkey, MXGE_HWRSS_KEYLEN);
33821cd61a7cSSepherosa Ziehau 			for (i = 0; i < MXGE_HWRSS_KEYLEN; ++i)
33831cd61a7cSSepherosa Ziehau 				hwkey[i] = swkey[i];
33841cd61a7cSSepherosa Ziehau 			wmb();
33851cd61a7cSSepherosa Ziehau 
33861cd61a7cSSepherosa Ziehau 			err = mxge_send_cmd(sc, MXGEFW_CMD_RSS_KEY_UPDATED,
33871cd61a7cSSepherosa Ziehau 			    &cmd);
33881cd61a7cSSepherosa Ziehau 			if (err != 0) {
33891cd61a7cSSepherosa Ziehau 				if_printf(ifp, "failed to update rsskey\n");
33901cd61a7cSSepherosa Ziehau 				return err;
33911cd61a7cSSepherosa Ziehau 			}
33921cd61a7cSSepherosa Ziehau 			if (bootverbose)
33931cd61a7cSSepherosa Ziehau 				if_printf(ifp, "RSS key updated\n");
33941cd61a7cSSepherosa Ziehau 		}
33951cd61a7cSSepherosa Ziehau 
33968892ea20SAggelos Economopoulos 		cmd.data0 = 1;
33978433e5f5SSepherosa Ziehau 		if (sc->use_rss) {
33988433e5f5SSepherosa Ziehau 			if (bootverbose)
33998433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: RSS\n");
3400e6c7b753SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_IPV4 |
3401e6c7b753SSepherosa Ziehau 			    MXGEFW_RSS_HASH_TYPE_TCP_IPV4;
34028433e5f5SSepherosa Ziehau 		} else {
34038433e5f5SSepherosa Ziehau 			if (bootverbose)
34048433e5f5SSepherosa Ziehau 				if_printf(ifp, "input hash: SRC_DST_PORT\n");
34058433e5f5SSepherosa Ziehau 			cmd.data1 = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
34068433e5f5SSepherosa Ziehau 		}
34078892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd);
34088892ea20SAggelos Economopoulos 		if (err != 0) {
34096ee6cba3SSepherosa Ziehau 			if_printf(ifp, "failed to enable slices\n");
34108892ea20SAggelos Economopoulos 			return err;
34118892ea20SAggelos Economopoulos 		}
34128892ea20SAggelos Economopoulos 	}
34138892ea20SAggelos Economopoulos 
341489d55360SSepherosa Ziehau 	cmd.data0 = MXGEFW_TSO_MODE_NDIS;
341589d55360SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_TSO_MODE, &cmd);
341689d55360SSepherosa Ziehau 	if (err) {
34176ee6cba3SSepherosa Ziehau 		/*
34186ee6cba3SSepherosa Ziehau 		 * Can't change TSO mode to NDIS, never allow TSO then
34196ee6cba3SSepherosa Ziehau 		 */
34206ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to set TSO mode\n");
34216ee6cba3SSepherosa Ziehau 		ifp->if_capenable &= ~IFCAP_TSO;
34226ee6cba3SSepherosa Ziehau 		ifp->if_capabilities &= ~IFCAP_TSO;
34236ee6cba3SSepherosa Ziehau 		ifp->if_hwassist &= ~CSUM_TSO;
342489d55360SSepherosa Ziehau 	}
34258892ea20SAggelos Economopoulos 
3426b9a8961fSSepherosa Ziehau 	mxge_choose_params(ifp->if_mtu, &cl_size);
34278892ea20SAggelos Economopoulos 
3428b9a8961fSSepherosa Ziehau 	cmd.data0 = 1;
34296ee6cba3SSepherosa Ziehau 	err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, &cmd);
34306ee6cba3SSepherosa Ziehau 	/*
34316ee6cba3SSepherosa Ziehau 	 * Error is only meaningful if we're trying to set
34326ee6cba3SSepherosa Ziehau 	 * MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1
34336ee6cba3SSepherosa Ziehau 	 */
34346ee6cba3SSepherosa Ziehau 
34356ee6cba3SSepherosa Ziehau 	/*
34366ee6cba3SSepherosa Ziehau 	 * Give the firmware the mtu and the big and small buffer
34376ee6cba3SSepherosa Ziehau 	 * sizes.  The firmware wants the big buf size to be a power
34382f47b54fSSepherosa Ziehau 	 * of two. Luckily, DragonFly's clusters are powers of two
34396ee6cba3SSepherosa Ziehau 	 */
34406ee6cba3SSepherosa Ziehau 	cmd.data0 = ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
34418892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd);
34426ee6cba3SSepherosa Ziehau 
344360f8c66dSSepherosa Ziehau 	cmd.data0 = MXGE_RX_SMALL_BUFLEN;
34446ee6cba3SSepherosa Ziehau 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd);
34456ee6cba3SSepherosa Ziehau 
3446b9a8961fSSepherosa Ziehau 	cmd.data0 = cl_size;
34478892ea20SAggelos Economopoulos 	err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd);
34488892ea20SAggelos Economopoulos 
34498892ea20SAggelos Economopoulos 	if (err != 0) {
34506ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
34518892ea20SAggelos Economopoulos 		goto abort;
34528892ea20SAggelos Economopoulos 	}
34538892ea20SAggelos Economopoulos 
34548892ea20SAggelos Economopoulos 	/* Now give him the pointer to the stats block */
3455ebe934eaSSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; slice++) {
34568892ea20SAggelos Economopoulos 		ss = &sc->ss[slice];
34577cc92483SSepherosa Ziehau 		cmd.data0 = MXGE_LOWPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34587cc92483SSepherosa Ziehau 		cmd.data1 = MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.dmem_busaddr);
34598892ea20SAggelos Economopoulos 		cmd.data2 = sizeof(struct mcp_irq_data);
34608892ea20SAggelos Economopoulos 		cmd.data2 |= (slice << 16);
34618892ea20SAggelos Economopoulos 		err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd);
34628892ea20SAggelos Economopoulos 	}
34638892ea20SAggelos Economopoulos 
34648892ea20SAggelos Economopoulos 	if (err != 0) {
34657cc92483SSepherosa Ziehau 		bus = sc->ss->fw_stats_dma.dmem_busaddr;
34668892ea20SAggelos Economopoulos 		bus += offsetof(struct mcp_irq_data, send_done_count);
34678892ea20SAggelos Economopoulos 		cmd.data0 = MXGE_LOWPART_TO_U32(bus);
34688892ea20SAggelos Economopoulos 		cmd.data1 = MXGE_HIGHPART_TO_U32(bus);
34696ee6cba3SSepherosa Ziehau 		err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
34708892ea20SAggelos Economopoulos 		    &cmd);
34716ee6cba3SSepherosa Ziehau 
34728892ea20SAggelos Economopoulos 		/* Firmware cannot support multicast without STATS_DMA_V2 */
34738892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 0;
34748892ea20SAggelos Economopoulos 	} else {
34758892ea20SAggelos Economopoulos 		sc->fw_multicast_support = 1;
34768892ea20SAggelos Economopoulos 	}
34778892ea20SAggelos Economopoulos 
34788892ea20SAggelos Economopoulos 	if (err != 0) {
34796ee6cba3SSepherosa Ziehau 		if_printf(ifp, "failed to setup params\n");
34808892ea20SAggelos Economopoulos 		goto abort;
34818892ea20SAggelos Economopoulos 	}
34828892ea20SAggelos Economopoulos 
34838892ea20SAggelos Economopoulos 	for (slice = 0; slice < sc->num_slices; slice++) {
3484b9a8961fSSepherosa Ziehau 		err = mxge_slice_open(&sc->ss[slice], cl_size);
34858892ea20SAggelos Economopoulos 		if (err != 0) {
34866ee6cba3SSepherosa Ziehau 			if_printf(ifp, "couldn't open slice %d\n", slice);
34878892ea20SAggelos Economopoulos 			goto abort;
34888892ea20SAggelos Economopoulos 		}
34898892ea20SAggelos Economopoulos 	}
34908892ea20SAggelos Economopoulos 
34918892ea20SAggelos Economopoulos 	/* Finally, start the firmware running */
34928892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd);
34938892ea20SAggelos Economopoulos 	if (err) {
34946ee6cba3SSepherosa Ziehau 		if_printf(ifp, "Couldn't bring up link\n");
34958892ea20SAggelos Economopoulos 		goto abort;
34968892ea20SAggelos Economopoulos 	}
3497aca8f373SSepherosa Ziehau 
34986ee6cba3SSepherosa Ziehau 	ifp->if_flags |= IFF_RUNNING;
3499aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3500aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3501aca8f373SSepherosa Ziehau 
3502aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3503aca8f373SSepherosa Ziehau 		ifsq_watchdog_start(&tx->watchdog);
3504aca8f373SSepherosa Ziehau 	}
35058892ea20SAggelos Economopoulos 
35068892ea20SAggelos Economopoulos 	return 0;
35078892ea20SAggelos Economopoulos 
35088892ea20SAggelos Economopoulos abort:
35098892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
35108892ea20SAggelos Economopoulos 	return err;
35118892ea20SAggelos Economopoulos }
35128892ea20SAggelos Economopoulos 
35132c29ffc6SSepherosa Ziehau static void
mxge_close(mxge_softc_t * sc,int down)351489d55360SSepherosa Ziehau mxge_close(mxge_softc_t *sc, int down)
35158892ea20SAggelos Economopoulos {
35162c29ffc6SSepherosa Ziehau 	struct ifnet *ifp = sc->ifp;
35178892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
3518aca8f373SSepherosa Ziehau 	int err, old_down_cnt, i;
35198892ea20SAggelos Economopoulos 
352026634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
352189d55360SSepherosa Ziehau 
352289d55360SSepherosa Ziehau 	if (!down) {
35238892ea20SAggelos Economopoulos 		old_down_cnt = sc->down_cnt;
35248892ea20SAggelos Economopoulos 		wmb();
35252c29ffc6SSepherosa Ziehau 
35268892ea20SAggelos Economopoulos 		err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd);
35272c29ffc6SSepherosa Ziehau 		if (err)
35282c29ffc6SSepherosa Ziehau 			if_printf(ifp, "Couldn't bring down link\n");
35292c29ffc6SSepherosa Ziehau 
35308892ea20SAggelos Economopoulos 		if (old_down_cnt == sc->down_cnt) {
3531aca8f373SSepherosa Ziehau 			/*
3532aca8f373SSepherosa Ziehau 			 * Wait for down irq
3533aca8f373SSepherosa Ziehau 			 * XXX racy
3534aca8f373SSepherosa Ziehau 			 */
353526634ef8SSepherosa Ziehau 			ifnet_deserialize_all(ifp);
35368892ea20SAggelos Economopoulos 			DELAY(10 * sc->intr_coal_delay);
353726634ef8SSepherosa Ziehau 			ifnet_serialize_all(ifp);
35388892ea20SAggelos Economopoulos 		}
35392c29ffc6SSepherosa Ziehau 
35408892ea20SAggelos Economopoulos 		wmb();
35412c29ffc6SSepherosa Ziehau 		if (old_down_cnt == sc->down_cnt)
35422c29ffc6SSepherosa Ziehau 			if_printf(ifp, "never got down irq\n");
354389d55360SSepherosa Ziehau 	}
35448892ea20SAggelos Economopoulos 	mxge_free_mbufs(sc);
3545aca8f373SSepherosa Ziehau 
3546aca8f373SSepherosa Ziehau 	ifp->if_flags &= ~IFF_RUNNING;
3547aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
3548aca8f373SSepherosa Ziehau 		mxge_tx_ring_t *tx = &sc->ss[i].tx;
3549aca8f373SSepherosa Ziehau 
3550aca8f373SSepherosa Ziehau 		ifsq_clr_oactive(tx->ifsq);
3551aca8f373SSepherosa Ziehau 		ifsq_watchdog_stop(&tx->watchdog);
3552aca8f373SSepherosa Ziehau 	}
35538892ea20SAggelos Economopoulos }
35548892ea20SAggelos Economopoulos 
35558892ea20SAggelos Economopoulos static void
mxge_setup_cfg_space(mxge_softc_t * sc)35568892ea20SAggelos Economopoulos mxge_setup_cfg_space(mxge_softc_t *sc)
35578892ea20SAggelos Economopoulos {
35588892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
35598892ea20SAggelos Economopoulos 	int reg;
356089d55360SSepherosa Ziehau 	uint16_t lnk, pectl;
35618892ea20SAggelos Economopoulos 
35627cc92483SSepherosa Ziehau 	/* Find the PCIe link width and set max read request to 4KB */
35638892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_EXPRESS, &reg) == 0) {
35648892ea20SAggelos Economopoulos 		lnk = pci_read_config(dev, reg + 0x12, 2);
35658892ea20SAggelos Economopoulos 		sc->link_width = (lnk >> 4) & 0x3f;
35668892ea20SAggelos Economopoulos 
356789d55360SSepherosa Ziehau 		if (sc->pectl == 0) {
35688892ea20SAggelos Economopoulos 			pectl = pci_read_config(dev, reg + 0x8, 2);
35698892ea20SAggelos Economopoulos 			pectl = (pectl & ~0x7000) | (5 << 12);
35708892ea20SAggelos Economopoulos 			pci_write_config(dev, reg + 0x8, pectl, 2);
357189d55360SSepherosa Ziehau 			sc->pectl = pectl;
357289d55360SSepherosa Ziehau 		} else {
35737cc92483SSepherosa Ziehau 			/* Restore saved pectl after watchdog reset */
357489d55360SSepherosa Ziehau 			pci_write_config(dev, reg + 0x8, sc->pectl, 2);
357589d55360SSepherosa Ziehau 		}
35768892ea20SAggelos Economopoulos 	}
35778892ea20SAggelos Economopoulos 
35787cc92483SSepherosa Ziehau 	/* Enable DMA and memory space access */
35798892ea20SAggelos Economopoulos 	pci_enable_busmaster(dev);
35808892ea20SAggelos Economopoulos }
35818892ea20SAggelos Economopoulos 
35828892ea20SAggelos Economopoulos static uint32_t
mxge_read_reboot(mxge_softc_t * sc)35838892ea20SAggelos Economopoulos mxge_read_reboot(mxge_softc_t *sc)
35848892ea20SAggelos Economopoulos {
35858892ea20SAggelos Economopoulos 	device_t dev = sc->dev;
35868892ea20SAggelos Economopoulos 	uint32_t vs;
35878892ea20SAggelos Economopoulos 
35888a20b038SSepherosa Ziehau 	/* Find the vendor specific offset */
35898892ea20SAggelos Economopoulos 	if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) {
35908a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "could not find vendor specific offset\n");
35918892ea20SAggelos Economopoulos 		return (uint32_t)-1;
35928892ea20SAggelos Economopoulos 	}
35938a20b038SSepherosa Ziehau 	/* Enable read32 mode */
35948892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x10, 0x3, 1);
35958a20b038SSepherosa Ziehau 	/* Tell NIC which register to read */
35968892ea20SAggelos Economopoulos 	pci_write_config(dev, vs + 0x18, 0xfffffff0, 4);
35978a20b038SSepherosa Ziehau 	return pci_read_config(dev, vs + 0x14, 4);
35988892ea20SAggelos Economopoulos }
35998892ea20SAggelos Economopoulos 
360089d55360SSepherosa Ziehau static void
mxge_watchdog_reset(mxge_softc_t * sc)360189d55360SSepherosa Ziehau mxge_watchdog_reset(mxge_softc_t *sc)
36028892ea20SAggelos Economopoulos {
36038892ea20SAggelos Economopoulos 	struct pci_devinfo *dinfo;
360489d55360SSepherosa Ziehau 	int err, running;
36058892ea20SAggelos Economopoulos 	uint32_t reboot;
36068892ea20SAggelos Economopoulos 	uint16_t cmd;
36078892ea20SAggelos Economopoulos 
36088892ea20SAggelos Economopoulos 	err = ENXIO;
36098892ea20SAggelos Economopoulos 
36108a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "Watchdog reset!\n");
36118892ea20SAggelos Economopoulos 
36128892ea20SAggelos Economopoulos 	/*
36138a20b038SSepherosa Ziehau 	 * Check to see if the NIC rebooted.  If it did, then all of
36148892ea20SAggelos Economopoulos 	 * PCI config space has been reset, and things like the
36158892ea20SAggelos Economopoulos 	 * busmaster bit will be zero.  If this is the case, then we
36168892ea20SAggelos Economopoulos 	 * must restore PCI config space before the NIC can be used
36178892ea20SAggelos Economopoulos 	 * again
36188892ea20SAggelos Economopoulos 	 */
36198892ea20SAggelos Economopoulos 	cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36208892ea20SAggelos Economopoulos 	if (cmd == 0xffff) {
36218892ea20SAggelos Economopoulos 		/*
36228a20b038SSepherosa Ziehau 		 * Maybe the watchdog caught the NIC rebooting; wait
36238892ea20SAggelos Economopoulos 		 * up to 100ms for it to finish.  If it does not come
36248892ea20SAggelos Economopoulos 		 * back, then give up
36258892ea20SAggelos Economopoulos 		 */
36268892ea20SAggelos Economopoulos 		DELAY(1000*100);
36278892ea20SAggelos Economopoulos 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
36288a20b038SSepherosa Ziehau 		if (cmd == 0xffff)
36298a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "NIC disappeared!\n");
36308892ea20SAggelos Economopoulos 	}
36318892ea20SAggelos Economopoulos 	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
36328a20b038SSepherosa Ziehau 		/* Print the reboot status */
36338892ea20SAggelos Economopoulos 		reboot = mxge_read_reboot(sc);
36348a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC rebooted, status = 0x%x\n", reboot);
36358a20b038SSepherosa Ziehau 
363689d55360SSepherosa Ziehau 		running = sc->ifp->if_flags & IFF_RUNNING;
363789d55360SSepherosa Ziehau 		if (running) {
363889d55360SSepherosa Ziehau 			/*
36398a20b038SSepherosa Ziehau 			 * Quiesce NIC so that TX routines will not try to
364089d55360SSepherosa Ziehau 			 * xmit after restoration of BAR
364189d55360SSepherosa Ziehau 			 */
364289d55360SSepherosa Ziehau 
364389d55360SSepherosa Ziehau 			/* Mark the link as down */
364489d55360SSepherosa Ziehau 			if (sc->link_state) {
364589d55360SSepherosa Ziehau 				sc->ifp->if_link_state = LINK_STATE_DOWN;
364689d55360SSepherosa Ziehau 				if_link_state_change(sc->ifp);
364789d55360SSepherosa Ziehau 			}
364889d55360SSepherosa Ziehau 			mxge_close(sc, 1);
364989d55360SSepherosa Ziehau 		}
36508a20b038SSepherosa Ziehau 		/* Restore PCI configuration space */
36518892ea20SAggelos Economopoulos 		dinfo = device_get_ivars(sc->dev);
36528892ea20SAggelos Economopoulos 		pci_cfg_restore(sc->dev, dinfo);
36538892ea20SAggelos Economopoulos 
36548a20b038SSepherosa Ziehau 		/* And redo any changes we made to our config space */
36558892ea20SAggelos Economopoulos 		mxge_setup_cfg_space(sc);
36568892ea20SAggelos Economopoulos 
36578a20b038SSepherosa Ziehau 		/* Reload f/w */
365889d55360SSepherosa Ziehau 		err = mxge_load_firmware(sc, 0);
36598a20b038SSepherosa Ziehau 		if (err)
36608a20b038SSepherosa Ziehau 			if_printf(sc->ifp, "Unable to re-load f/w\n");
36618a20b038SSepherosa Ziehau 		if (running && !err) {
3662aca8f373SSepherosa Ziehau 			int i;
3663aca8f373SSepherosa Ziehau 
366489d55360SSepherosa Ziehau 			err = mxge_open(sc);
3665aca8f373SSepherosa Ziehau 
3666aca8f373SSepherosa Ziehau 			for (i = 0; i < sc->num_tx_rings; ++i)
3667aca8f373SSepherosa Ziehau 				ifsq_devstart_sched(sc->ss[i].tx.ifsq);
366889d55360SSepherosa Ziehau 		}
366989d55360SSepherosa Ziehau 		sc->watchdog_resets++;
367089d55360SSepherosa Ziehau 	} else {
36718a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "NIC did not reboot, not resetting\n");
367289d55360SSepherosa Ziehau 		err = 0;
367389d55360SSepherosa Ziehau 	}
367489d55360SSepherosa Ziehau 	if (err) {
36758a20b038SSepherosa Ziehau 		if_printf(sc->ifp, "watchdog reset failed\n");
367689d55360SSepherosa Ziehau 	} else {
367789d55360SSepherosa Ziehau 		if (sc->dying == 2)
367889d55360SSepherosa Ziehau 			sc->dying = 0;
367989d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc);
368089d55360SSepherosa Ziehau 	}
368189d55360SSepherosa Ziehau }
368289d55360SSepherosa Ziehau 
368389d55360SSepherosa Ziehau static void
mxge_warn_stuck(mxge_softc_t * sc,mxge_tx_ring_t * tx,int slice)368489d55360SSepherosa Ziehau mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice)
368589d55360SSepherosa Ziehau {
36868a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "slice %d struck? ring state:\n", slice);
36878a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.req=%d tx.done=%d, tx.queue_active=%d\n",
36888892ea20SAggelos Economopoulos 	    tx->req, tx->done, tx->queue_active);
36898a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "tx.activate=%d tx.deactivate=%d\n",
36908892ea20SAggelos Economopoulos 	    tx->activate, tx->deactivate);
36918a20b038SSepherosa Ziehau 	if_printf(sc->ifp, "pkt_done=%d fw=%d\n",
36928a20b038SSepherosa Ziehau 	    tx->pkt_done, be32toh(sc->ss->fw_stats->send_done_count));
36938892ea20SAggelos Economopoulos }
36948892ea20SAggelos Economopoulos 
369589d55360SSepherosa Ziehau static u_long
mxge_update_stats(mxge_softc_t * sc)36968892ea20SAggelos Economopoulos mxge_update_stats(mxge_softc_t *sc)
36978892ea20SAggelos Economopoulos {
3698cc9c62a4SSepherosa Ziehau 	u_long ipackets, opackets, pkts;
36998892ea20SAggelos Economopoulos 
3700cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, ipackets, ipackets);
3701cc9c62a4SSepherosa Ziehau 	IFNET_STAT_GET(sc->ifp, opackets, opackets);
370289d55360SSepherosa Ziehau 
3703cc9c62a4SSepherosa Ziehau 	pkts = ipackets - sc->ipackets;
3704cc9c62a4SSepherosa Ziehau 	pkts += opackets - sc->opackets;
370589d55360SSepherosa Ziehau 
3706cc9c62a4SSepherosa Ziehau 	sc->ipackets = ipackets;
3707cc9c62a4SSepherosa Ziehau 	sc->opackets = opackets;
3708cc9c62a4SSepherosa Ziehau 
370989d55360SSepherosa Ziehau 	return pkts;
37108892ea20SAggelos Economopoulos }
37118892ea20SAggelos Economopoulos 
37128892ea20SAggelos Economopoulos static void
mxge_tick(void * arg)37138892ea20SAggelos Economopoulos mxge_tick(void *arg)
37148892ea20SAggelos Economopoulos {
37158892ea20SAggelos Economopoulos 	mxge_softc_t *sc = arg;
371689d55360SSepherosa Ziehau 	u_long pkts = 0;
37178892ea20SAggelos Economopoulos 	int err = 0;
3718ca8ca004SSepherosa Ziehau 	int ticks;
37198892ea20SAggelos Economopoulos 
372026634ef8SSepherosa Ziehau 	lwkt_serialize_enter(&sc->main_serialize);
372189d55360SSepherosa Ziehau 
372289d55360SSepherosa Ziehau 	ticks = mxge_ticks;
3723ca8ca004SSepherosa Ziehau 	if (sc->ifp->if_flags & IFF_RUNNING) {
3724ca8ca004SSepherosa Ziehau 		/* Aggregate stats from different slices */
372589d55360SSepherosa Ziehau 		pkts = mxge_update_stats(sc);
3726ca8ca004SSepherosa Ziehau 		if (sc->need_media_probe)
3727ca8ca004SSepherosa Ziehau 			mxge_media_probe(sc);
372889d55360SSepherosa Ziehau 	}
372989d55360SSepherosa Ziehau 	if (pkts == 0) {
3730cc9c62a4SSepherosa Ziehau 		uint16_t cmd;
3731cc9c62a4SSepherosa Ziehau 
3732ca8ca004SSepherosa Ziehau 		/* Ensure NIC did not suffer h/w fault while idle */
373389d55360SSepherosa Ziehau 		cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2);
373489d55360SSepherosa Ziehau 		if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
373589d55360SSepherosa Ziehau 			sc->dying = 2;
373626634ef8SSepherosa Ziehau 			mxge_serialize_skipmain(sc);
373789d55360SSepherosa Ziehau 			mxge_watchdog_reset(sc);
373826634ef8SSepherosa Ziehau 			mxge_deserialize_skipmain(sc);
373989d55360SSepherosa Ziehau 			err = ENXIO;
374089d55360SSepherosa Ziehau 		}
3741cc9c62a4SSepherosa Ziehau 
3742ca8ca004SSepherosa Ziehau 		/* Look less often if NIC is idle */
374389d55360SSepherosa Ziehau 		ticks *= 4;
374489d55360SSepherosa Ziehau 	}
374589d55360SSepherosa Ziehau 
37468892ea20SAggelos Economopoulos 	if (err == 0)
374789d55360SSepherosa Ziehau 		callout_reset(&sc->co_hdl, ticks, mxge_tick, sc);
374889d55360SSepherosa Ziehau 
374926634ef8SSepherosa Ziehau 	lwkt_serialize_exit(&sc->main_serialize);
37508892ea20SAggelos Economopoulos }
37518892ea20SAggelos Economopoulos 
37528892ea20SAggelos Economopoulos static int
mxge_media_change(struct ifnet * ifp)37538892ea20SAggelos Economopoulos mxge_media_change(struct ifnet *ifp)
37548892ea20SAggelos Economopoulos {
375500f2de12SSepherosa Ziehau 	mxge_softc_t *sc = ifp->if_softc;
375600f2de12SSepherosa Ziehau 	const struct ifmedia *ifm = &sc->media;
375700f2de12SSepherosa Ziehau 	int pause;
375800f2de12SSepherosa Ziehau 
375900f2de12SSepherosa Ziehau 	if (IFM_OPTIONS(ifm->ifm_media) & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE)) {
376000f2de12SSepherosa Ziehau 		if (sc->pause)
376100f2de12SSepherosa Ziehau 			return 0;
376200f2de12SSepherosa Ziehau 		pause = 1;
376300f2de12SSepherosa Ziehau 	} else {
376400f2de12SSepherosa Ziehau 		if (!sc->pause)
376500f2de12SSepherosa Ziehau 			return 0;
376600f2de12SSepherosa Ziehau 		pause = 0;
376700f2de12SSepherosa Ziehau 	}
376800f2de12SSepherosa Ziehau 	return mxge_change_pause(sc, pause);
37698892ea20SAggelos Economopoulos }
37708892ea20SAggelos Economopoulos 
37718892ea20SAggelos Economopoulos static int
mxge_change_mtu(mxge_softc_t * sc,int mtu)37728892ea20SAggelos Economopoulos mxge_change_mtu(mxge_softc_t *sc, int mtu)
37738892ea20SAggelos Economopoulos {
37748892ea20SAggelos Economopoulos 	struct ifnet *ifp = sc->ifp;
37758892ea20SAggelos Economopoulos 	int real_mtu, old_mtu;
37768892ea20SAggelos Economopoulos 	int err = 0;
37778892ea20SAggelos Economopoulos 
3778b915556eSAggelos Economopoulos 	real_mtu = mtu + ETHER_HDR_LEN + EVL_ENCAPLEN;
3779b9a8961fSSepherosa Ziehau 	if (mtu > sc->max_mtu || real_mtu < 60)
37808892ea20SAggelos Economopoulos 		return EINVAL;
3781b9a8961fSSepherosa Ziehau 
37828892ea20SAggelos Economopoulos 	old_mtu = ifp->if_mtu;
37838892ea20SAggelos Economopoulos 	ifp->if_mtu = mtu;
37842ab1b8a9SAggelos Economopoulos 	if (ifp->if_flags & IFF_RUNNING) {
378589d55360SSepherosa Ziehau 		mxge_close(sc, 0);
37868892ea20SAggelos Economopoulos 		err = mxge_open(sc);
37878892ea20SAggelos Economopoulos 		if (err != 0) {
37888892ea20SAggelos Economopoulos 			ifp->if_mtu = old_mtu;
378989d55360SSepherosa Ziehau 			mxge_close(sc, 0);
3790b9a8961fSSepherosa Ziehau 			mxge_open(sc);
37918892ea20SAggelos Economopoulos 		}
37928892ea20SAggelos Economopoulos 	}
37938892ea20SAggelos Economopoulos 	return err;
37948892ea20SAggelos Economopoulos }
37958892ea20SAggelos Economopoulos 
37968892ea20SAggelos Economopoulos static void
mxge_media_status(struct ifnet * ifp,struct ifmediareq * ifmr)37978892ea20SAggelos Economopoulos mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
37988892ea20SAggelos Economopoulos {
37998892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38008892ea20SAggelos Economopoulos 
38018892ea20SAggelos Economopoulos 	ifmr->ifm_status = IFM_AVALID;
3802166c46afSSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
3803166c46afSSepherosa Ziehau 
3804dbf0d419SSepherosa Ziehau 	if (sc->link_state)
3805166c46afSSepherosa Ziehau 		ifmr->ifm_status |= IFM_ACTIVE;
3806166c46afSSepherosa Ziehau 
3807dbf0d419SSepherosa Ziehau 	/*
3808dbf0d419SSepherosa Ziehau 	 * Autoselect is not supported, so the current media
3809dbf0d419SSepherosa Ziehau 	 * should be delivered.
3810dbf0d419SSepherosa Ziehau 	 */
381189d55360SSepherosa Ziehau 	ifmr->ifm_active |= sc->current_media;
381200f2de12SSepherosa Ziehau 	if (sc->current_media != IFM_NONE) {
381300f2de12SSepherosa Ziehau 		ifmr->ifm_active |= MXGE_IFM;
381400f2de12SSepherosa Ziehau 		if (sc->pause)
381500f2de12SSepherosa Ziehau 			ifmr->ifm_active |= IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
381600f2de12SSepherosa Ziehau 	}
38178892ea20SAggelos Economopoulos }
38188892ea20SAggelos Economopoulos 
38198892ea20SAggelos Economopoulos static int
mxge_ioctl(struct ifnet * ifp,u_long command,caddr_t data,struct ucred * cr __unused)382089d55360SSepherosa Ziehau mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data,
382189d55360SSepherosa Ziehau     struct ucred *cr __unused)
38228892ea20SAggelos Economopoulos {
38238892ea20SAggelos Economopoulos 	mxge_softc_t *sc = ifp->if_softc;
38248892ea20SAggelos Economopoulos 	struct ifreq *ifr = (struct ifreq *)data;
38258892ea20SAggelos Economopoulos 	int err, mask;
38268892ea20SAggelos Economopoulos 
382726634ef8SSepherosa Ziehau 	ASSERT_IFNET_SERIALIZED_ALL(ifp);
3828af85d4d5SSepherosa Ziehau 	err = 0;
3829af85d4d5SSepherosa Ziehau 
38308892ea20SAggelos Economopoulos 	switch (command) {
38318892ea20SAggelos Economopoulos 	case SIOCSIFMTU:
38328892ea20SAggelos Economopoulos 		err = mxge_change_mtu(sc, ifr->ifr_mtu);
38338892ea20SAggelos Economopoulos 		break;
38348892ea20SAggelos Economopoulos 
38358892ea20SAggelos Economopoulos 	case SIOCSIFFLAGS:
3836af85d4d5SSepherosa Ziehau 		if (sc->dying)
38378892ea20SAggelos Economopoulos 			return EINVAL;
3838af85d4d5SSepherosa Ziehau 
38398892ea20SAggelos Economopoulos 		if (ifp->if_flags & IFF_UP) {
38402ab1b8a9SAggelos Economopoulos 			if (!(ifp->if_flags & IFF_RUNNING)) {
38418892ea20SAggelos Economopoulos 				err = mxge_open(sc);
38428892ea20SAggelos Economopoulos 			} else {
3843af85d4d5SSepherosa Ziehau 				/*
3844af85d4d5SSepherosa Ziehau 				 * Take care of PROMISC and ALLMULTI
3845af85d4d5SSepherosa Ziehau 				 * flag changes
3846af85d4d5SSepherosa Ziehau 				 */
38478892ea20SAggelos Economopoulos 				mxge_change_promisc(sc,
38488892ea20SAggelos Economopoulos 				    ifp->if_flags & IFF_PROMISC);
38498892ea20SAggelos Economopoulos 				mxge_set_multicast_list(sc);
38508892ea20SAggelos Economopoulos 			}
38518892ea20SAggelos Economopoulos 		} else {
3852af85d4d5SSepherosa Ziehau 			if (ifp->if_flags & IFF_RUNNING)
385389d55360SSepherosa Ziehau 				mxge_close(sc, 0);
38548892ea20SAggelos Economopoulos 		}
38558892ea20SAggelos Economopoulos 		break;
38568892ea20SAggelos Economopoulos 
38578892ea20SAggelos Economopoulos 	case SIOCADDMULTI:
38588892ea20SAggelos Economopoulos 	case SIOCDELMULTI:
38598892ea20SAggelos Economopoulos 		mxge_set_multicast_list(sc);
38608892ea20SAggelos Economopoulos 		break;
38618892ea20SAggelos Economopoulos 
38628892ea20SAggelos Economopoulos 	case SIOCSIFCAP:
38638892ea20SAggelos Economopoulos 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
38648892ea20SAggelos Economopoulos 		if (mask & IFCAP_TXCSUM) {
386589d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
386689d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
386789d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TCP | CSUM_UDP;
38688892ea20SAggelos Economopoulos 			else
386989d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP);
38708892ea20SAggelos Economopoulos 		}
387189d55360SSepherosa Ziehau 		if (mask & IFCAP_TSO) {
387289d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO;
387389d55360SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO)
387489d55360SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_TSO;
387589d55360SSepherosa Ziehau 			else
387689d55360SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_TSO;
387789d55360SSepherosa Ziehau 		}
387889d55360SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
387989d55360SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
38808892ea20SAggelos Economopoulos 		if (mask & IFCAP_VLAN_HWTAGGING)
38818892ea20SAggelos Economopoulos 			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
38828892ea20SAggelos Economopoulos 		break;
38838892ea20SAggelos Economopoulos 
38848892ea20SAggelos Economopoulos 	case SIOCGIFMEDIA:
388500f2de12SSepherosa Ziehau 	case SIOCSIFMEDIA:
38868892ea20SAggelos Economopoulos 		err = ifmedia_ioctl(ifp, (struct ifreq *)data,
38878892ea20SAggelos Economopoulos 		    &sc->media, command);
38888892ea20SAggelos Economopoulos 		break;
38898892ea20SAggelos Economopoulos 
38908892ea20SAggelos Economopoulos 	default:
389189d55360SSepherosa Ziehau 		err = ether_ioctl(ifp, command, data);
389289d55360SSepherosa Ziehau 		break;
38938892ea20SAggelos Economopoulos 	}
38948892ea20SAggelos Economopoulos 	return err;
38958892ea20SAggelos Economopoulos }
38968892ea20SAggelos Economopoulos 
38978892ea20SAggelos Economopoulos static void
mxge_fetch_tunables(mxge_softc_t * sc)38988892ea20SAggelos Economopoulos mxge_fetch_tunables(mxge_softc_t *sc)
38998892ea20SAggelos Economopoulos {
390000f2de12SSepherosa Ziehau 	int ifm;
390100f2de12SSepherosa Ziehau 
39027cc92483SSepherosa Ziehau 	sc->intr_coal_delay = mxge_intr_coal_delay;
39037cc92483SSepherosa Ziehau 	if (sc->intr_coal_delay < 0 || sc->intr_coal_delay > (10 * 1000))
39047cc92483SSepherosa Ziehau 		sc->intr_coal_delay = MXGE_INTR_COAL_DELAY;
39058892ea20SAggelos Economopoulos 
39067cc92483SSepherosa Ziehau 	/* XXX */
39078892ea20SAggelos Economopoulos 	if (mxge_ticks == 0)
39088892ea20SAggelos Economopoulos 		mxge_ticks = hz / 2;
39097cc92483SSepherosa Ziehau 
391000f2de12SSepherosa Ziehau 	ifm = ifmedia_str2ethfc(mxge_flowctrl);
391100f2de12SSepherosa Ziehau 	if (ifm & (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE))
391200f2de12SSepherosa Ziehau 		sc->pause = 1;
391300f2de12SSepherosa Ziehau 
39148433e5f5SSepherosa Ziehau 	sc->use_rss = mxge_use_rss;
39158892ea20SAggelos Economopoulos 
391689d55360SSepherosa Ziehau 	sc->throttle = mxge_throttle;
39177cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle > MXGE_MAX_THROTTLE)
39187cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MAX_THROTTLE;
39197cc92483SSepherosa Ziehau 	if (sc->throttle && sc->throttle < MXGE_MIN_THROTTLE)
39207cc92483SSepherosa Ziehau 		sc->throttle = MXGE_MIN_THROTTLE;
392189d55360SSepherosa Ziehau }
39228892ea20SAggelos Economopoulos 
39238892ea20SAggelos Economopoulos static void
mxge_free_slices(mxge_softc_t * sc)39248892ea20SAggelos Economopoulos mxge_free_slices(mxge_softc_t *sc)
39258892ea20SAggelos Economopoulos {
39268892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39278892ea20SAggelos Economopoulos 	int i;
39288892ea20SAggelos Economopoulos 
39298892ea20SAggelos Economopoulos 	if (sc->ss == NULL)
39308892ea20SAggelos Economopoulos 		return;
39318892ea20SAggelos Economopoulos 
39328892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39338892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39348892ea20SAggelos Economopoulos 		if (ss->fw_stats != NULL) {
39358892ea20SAggelos Economopoulos 			mxge_dma_free(&ss->fw_stats_dma);
39368892ea20SAggelos Economopoulos 			ss->fw_stats = NULL;
39378892ea20SAggelos Economopoulos 		}
39389a4ae890SSepherosa Ziehau 		if (ss->rx_data.rx_done.entry != NULL) {
3939414caf0dSSepherosa Ziehau 			mxge_dma_free(&ss->rx_done_dma);
39409a4ae890SSepherosa Ziehau 			ss->rx_data.rx_done.entry = NULL;
39418892ea20SAggelos Economopoulos 		}
39428892ea20SAggelos Economopoulos 	}
39436c348da6SAggelos Economopoulos 	kfree(sc->ss, M_DEVBUF);
39448892ea20SAggelos Economopoulos 	sc->ss = NULL;
39458892ea20SAggelos Economopoulos }
39468892ea20SAggelos Economopoulos 
39478892ea20SAggelos Economopoulos static int
mxge_alloc_slices(mxge_softc_t * sc)39488892ea20SAggelos Economopoulos mxge_alloc_slices(mxge_softc_t *sc)
39498892ea20SAggelos Economopoulos {
39508892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
39518892ea20SAggelos Economopoulos 	struct mxge_slice_state *ss;
39528892ea20SAggelos Economopoulos 	size_t bytes;
3953089301c2SSepherosa Ziehau 	int err, i, rx_ring_size;
39548892ea20SAggelos Economopoulos 
39558892ea20SAggelos Economopoulos 	err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
39568892ea20SAggelos Economopoulos 	if (err != 0) {
39578892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
39588892ea20SAggelos Economopoulos 		return err;
39598892ea20SAggelos Economopoulos 	}
3960089301c2SSepherosa Ziehau 	rx_ring_size = cmd.data0;
3961089301c2SSepherosa Ziehau 	sc->rx_intr_slots = 2 * (rx_ring_size / sizeof (mcp_dma_addr_t));
39628892ea20SAggelos Economopoulos 
39638892ea20SAggelos Economopoulos 	bytes = sizeof(*sc->ss) * sc->num_slices;
396462938642SMatthew Dillon 	sc->ss = kmalloc(bytes, M_DEVBUF,
396562938642SMatthew Dillon 			 M_WAITOK | M_ZERO | M_CACHEALIGN);
39667cc92483SSepherosa Ziehau 
39678892ea20SAggelos Economopoulos 	for (i = 0; i < sc->num_slices; i++) {
39688892ea20SAggelos Economopoulos 		ss = &sc->ss[i];
39698892ea20SAggelos Economopoulos 
39708892ea20SAggelos Economopoulos 		ss->sc = sc;
39718892ea20SAggelos Economopoulos 
397226634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->rx_data.rx_serialize);
397326634ef8SSepherosa Ziehau 		lwkt_serialize_init(&ss->tx.tx_serialize);
3974e6c7b753SSepherosa Ziehau 		ss->intr_rid = -1;
397526634ef8SSepherosa Ziehau 
39767cc92483SSepherosa Ziehau 		/*
3977e6c7b753SSepherosa Ziehau 		 * Allocate per-slice rx interrupt queue
3978089301c2SSepherosa Ziehau 		 * XXX assume 4bytes mcp_slot
39797cc92483SSepherosa Ziehau 		 */
3980089301c2SSepherosa Ziehau 		bytes = sc->rx_intr_slots * sizeof(mcp_slot_t);
3981414caf0dSSepherosa Ziehau 		err = mxge_dma_alloc(sc, &ss->rx_done_dma, bytes, 4096);
3982798c3369SSepherosa Ziehau 		if (err != 0) {
3983798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3984798c3369SSepherosa Ziehau 			    "alloc %d slice rx_done failed\n", i);
3985798c3369SSepherosa Ziehau 			return err;
3986798c3369SSepherosa Ziehau 		}
3987414caf0dSSepherosa Ziehau 		ss->rx_data.rx_done.entry = ss->rx_done_dma.dmem_addr;
39888892ea20SAggelos Economopoulos 
39898892ea20SAggelos Economopoulos 		/*
3990ebe934eaSSepherosa Ziehau 		 * Allocate the per-slice firmware stats
39918892ea20SAggelos Economopoulos 		 */
39928892ea20SAggelos Economopoulos 		bytes = sizeof(*ss->fw_stats);
39938892ea20SAggelos Economopoulos 		err = mxge_dma_alloc(sc, &ss->fw_stats_dma,
39948892ea20SAggelos Economopoulos 		    sizeof(*ss->fw_stats), 64);
3995798c3369SSepherosa Ziehau 		if (err != 0) {
3996798c3369SSepherosa Ziehau 			device_printf(sc->dev,
3997798c3369SSepherosa Ziehau 			    "alloc %d fw_stats failed\n", i);
3998798c3369SSepherosa Ziehau 			return err;
3999798c3369SSepherosa Ziehau 		}
40007cc92483SSepherosa Ziehau 		ss->fw_stats = ss->fw_stats_dma.dmem_addr;
40018892ea20SAggelos Economopoulos 	}
40027cc92483SSepherosa Ziehau 	return 0;
40038892ea20SAggelos Economopoulos }
40048892ea20SAggelos Economopoulos 
40058892ea20SAggelos Economopoulos static void
mxge_slice_probe(mxge_softc_t * sc)40068892ea20SAggelos Economopoulos mxge_slice_probe(mxge_softc_t *sc)
40078892ea20SAggelos Economopoulos {
4008e6c7b753SSepherosa Ziehau 	int status, max_intr_slots, max_slices, num_slices;
4009bdbc20adSSepherosa Ziehau 	int msix_cnt, msix_enable, multi_tx;
40108892ea20SAggelos Economopoulos 	mxge_cmd_t cmd;
4011c7431c78SSepherosa Ziehau 	const char *old_fw;
40128892ea20SAggelos Economopoulos 
40138892ea20SAggelos Economopoulos 	sc->num_slices = 1;
4014aca8f373SSepherosa Ziehau 	sc->num_tx_rings = 1;
40157cc92483SSepherosa Ziehau 
4016e6c7b753SSepherosa Ziehau 	num_slices = device_getenv_int(sc->dev, "num_slices", mxge_num_slices);
4017e6c7b753SSepherosa Ziehau 	if (num_slices == 1)
40188892ea20SAggelos Economopoulos 		return;
40198892ea20SAggelos Economopoulos 
4020bdbc20adSSepherosa Ziehau 	if (netisr_ncpus == 1)
4021e6c7b753SSepherosa Ziehau 		return;
4022e6c7b753SSepherosa Ziehau 
4023e6c7b753SSepherosa Ziehau 	msix_enable = device_getenv_int(sc->dev, "msix.enable",
4024e6c7b753SSepherosa Ziehau 	    mxge_msix_enable);
4025e6c7b753SSepherosa Ziehau 	if (!msix_enable)
4026e6c7b753SSepherosa Ziehau 		return;
4027e6c7b753SSepherosa Ziehau 
40288892ea20SAggelos Economopoulos 	msix_cnt = pci_msix_count(sc->dev);
40298892ea20SAggelos Economopoulos 	if (msix_cnt < 2)
40308892ea20SAggelos Economopoulos 		return;
4031bdbc20adSSepherosa Ziehau 	if (bootverbose)
4032bdbc20adSSepherosa Ziehau 		device_printf(sc->dev, "MSI-X count %d\n", msix_cnt);
4033e6c7b753SSepherosa Ziehau 
4034e6c7b753SSepherosa Ziehau 	/*
4035e6c7b753SSepherosa Ziehau 	 * Now load the slice aware firmware see what it supports
4036e6c7b753SSepherosa Ziehau 	 */
40378892ea20SAggelos Economopoulos 	old_fw = sc->fw_name;
40388892ea20SAggelos Economopoulos 	if (old_fw == mxge_fw_aligned)
40398892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_aligned;
40408892ea20SAggelos Economopoulos 	else
40418892ea20SAggelos Economopoulos 		sc->fw_name = mxge_fw_rss_unaligned;
40428892ea20SAggelos Economopoulos 	status = mxge_load_firmware(sc, 0);
40438892ea20SAggelos Economopoulos 	if (status != 0) {
40448892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Falling back to a single slice\n");
40458892ea20SAggelos Economopoulos 		return;
40468892ea20SAggelos Economopoulos 	}
40478892ea20SAggelos Economopoulos 
4048e6c7b753SSepherosa Ziehau 	/*
4049e6c7b753SSepherosa Ziehau 	 * Try to send a reset command to the card to see if it is alive
4050e6c7b753SSepherosa Ziehau 	 */
40518892ea20SAggelos Economopoulos 	memset(&cmd, 0, sizeof(cmd));
40528892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd);
40538892ea20SAggelos Economopoulos 	if (status != 0) {
40548892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed reset\n");
40558892ea20SAggelos Economopoulos 		goto abort_with_fw;
40568892ea20SAggelos Economopoulos 	}
40578892ea20SAggelos Economopoulos 
4058e6c7b753SSepherosa Ziehau 	/*
4059e6c7b753SSepherosa Ziehau 	 * Get rx ring size to calculate rx interrupt queue size
4060e6c7b753SSepherosa Ziehau 	 */
40618892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd);
40628892ea20SAggelos Economopoulos 	if (status != 0) {
40638892ea20SAggelos Economopoulos 		device_printf(sc->dev, "Cannot determine rx ring size\n");
40648892ea20SAggelos Economopoulos 		goto abort_with_fw;
40658892ea20SAggelos Economopoulos 	}
40668892ea20SAggelos Economopoulos 	max_intr_slots = 2 * (cmd.data0 / sizeof(mcp_dma_addr_t));
40678892ea20SAggelos Economopoulos 
4068e6c7b753SSepherosa Ziehau 	/*
4069e6c7b753SSepherosa Ziehau 	 * Tell it the size of the rx interrupt queue
4070e6c7b753SSepherosa Ziehau 	 */
40718892ea20SAggelos Economopoulos 	cmd.data0 = max_intr_slots * sizeof(struct mcp_slot);
40728892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd);
40738892ea20SAggelos Economopoulos 	if (status != 0) {
40748892ea20SAggelos Economopoulos 		device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n");
40758892ea20SAggelos Economopoulos 		goto abort_with_fw;
40768892ea20SAggelos Economopoulos 	}
40778892ea20SAggelos Economopoulos 
4078e6c7b753SSepherosa Ziehau 	/*
4079e6c7b753SSepherosa Ziehau 	 * Ask the maximum number of slices it supports
4080e6c7b753SSepherosa Ziehau 	 */
40818892ea20SAggelos Economopoulos 	status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd);
40828892ea20SAggelos Economopoulos 	if (status != 0) {
40838892ea20SAggelos Economopoulos 		device_printf(sc->dev,
40848892ea20SAggelos Economopoulos 		    "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n");
40858892ea20SAggelos Economopoulos 		goto abort_with_fw;
40868892ea20SAggelos Economopoulos 	}
4087e6c7b753SSepherosa Ziehau 	max_slices = cmd.data0;
4088bdbc20adSSepherosa Ziehau 	if (bootverbose)
4089bdbc20adSSepherosa Ziehau 		device_printf(sc->dev, "max slices %d\n", max_slices);
4090e6c7b753SSepherosa Ziehau 
4091e6c7b753SSepherosa Ziehau 	if (max_slices > msix_cnt)
4092e6c7b753SSepherosa Ziehau 		max_slices = msix_cnt;
4093e6c7b753SSepherosa Ziehau 
4094bdbc20adSSepherosa Ziehau 	sc->ring_map = if_ringmap_alloc(sc->dev, num_slices, max_slices);
4095bdbc20adSSepherosa Ziehau 	sc->num_slices = if_ringmap_count(sc->ring_map);
4096e6c7b753SSepherosa Ziehau 
4097aca8f373SSepherosa Ziehau 	multi_tx = device_getenv_int(sc->dev, "multi_tx", mxge_multi_tx);
4098aca8f373SSepherosa Ziehau 	if (multi_tx)
4099aca8f373SSepherosa Ziehau 		sc->num_tx_rings = sc->num_slices;
4100aca8f373SSepherosa Ziehau 
4101e6c7b753SSepherosa Ziehau 	if (bootverbose) {
4102e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "using %d slices, max %d\n",
4103e6c7b753SSepherosa Ziehau 		    sc->num_slices, max_slices);
41048892ea20SAggelos Economopoulos 	}
41058892ea20SAggelos Economopoulos 
4106e6c7b753SSepherosa Ziehau 	if (sc->num_slices == 1)
4107e6c7b753SSepherosa Ziehau 		goto abort_with_fw;
41088892ea20SAggelos Economopoulos 	return;
41098892ea20SAggelos Economopoulos 
41108892ea20SAggelos Economopoulos abort_with_fw:
41118892ea20SAggelos Economopoulos 	sc->fw_name = old_fw;
4112e6c7b753SSepherosa Ziehau 	mxge_load_firmware(sc, 0);
41138892ea20SAggelos Economopoulos }
41148892ea20SAggelos Economopoulos 
411526634ef8SSepherosa Ziehau static void
mxge_setup_serialize(struct mxge_softc * sc)411626634ef8SSepherosa Ziehau mxge_setup_serialize(struct mxge_softc *sc)
411726634ef8SSepherosa Ziehau {
411826634ef8SSepherosa Ziehau 	int i = 0, slice;
411926634ef8SSepherosa Ziehau 
412026634ef8SSepherosa Ziehau 	/* Main + rx + tx */
412126634ef8SSepherosa Ziehau 	sc->nserialize = (2 * sc->num_slices) + 1;
412226634ef8SSepherosa Ziehau 	sc->serializes =
412326634ef8SSepherosa Ziehau 	    kmalloc(sc->nserialize * sizeof(struct lwkt_serialize *),
412426634ef8SSepherosa Ziehau 	        M_DEVBUF, M_WAITOK | M_ZERO);
412526634ef8SSepherosa Ziehau 
412626634ef8SSepherosa Ziehau 	/*
412726634ef8SSepherosa Ziehau 	 * Setup serializes
412826634ef8SSepherosa Ziehau 	 *
412926634ef8SSepherosa Ziehau 	 * NOTE: Order is critical
413026634ef8SSepherosa Ziehau 	 */
413126634ef8SSepherosa Ziehau 
413226634ef8SSepherosa Ziehau 	KKASSERT(i < sc->nserialize);
413326634ef8SSepherosa Ziehau 	sc->serializes[i++] = &sc->main_serialize;
413426634ef8SSepherosa Ziehau 
413526634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
413626634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
413726634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].rx_data.rx_serialize;
413826634ef8SSepherosa Ziehau 	}
413926634ef8SSepherosa Ziehau 
414026634ef8SSepherosa Ziehau 	for (slice = 0; slice < sc->num_slices; ++slice) {
414126634ef8SSepherosa Ziehau 		KKASSERT(i < sc->nserialize);
414226634ef8SSepherosa Ziehau 		sc->serializes[i++] = &sc->ss[slice].tx.tx_serialize;
414326634ef8SSepherosa Ziehau 	}
414426634ef8SSepherosa Ziehau 
414526634ef8SSepherosa Ziehau 	KKASSERT(i == sc->nserialize);
414626634ef8SSepherosa Ziehau }
414726634ef8SSepherosa Ziehau 
414826634ef8SSepherosa Ziehau static void
mxge_serialize(struct ifnet * ifp,enum ifnet_serialize slz)414926634ef8SSepherosa Ziehau mxge_serialize(struct ifnet *ifp, enum ifnet_serialize slz)
415026634ef8SSepherosa Ziehau {
415126634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
415226634ef8SSepherosa Ziehau 
415326634ef8SSepherosa Ziehau 	ifnet_serialize_array_enter(sc->serializes, sc->nserialize, slz);
415426634ef8SSepherosa Ziehau }
415526634ef8SSepherosa Ziehau 
415626634ef8SSepherosa Ziehau static void
mxge_deserialize(struct ifnet * ifp,enum ifnet_serialize slz)415726634ef8SSepherosa Ziehau mxge_deserialize(struct ifnet *ifp, enum ifnet_serialize slz)
415826634ef8SSepherosa Ziehau {
415926634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
416026634ef8SSepherosa Ziehau 
416126634ef8SSepherosa Ziehau 	ifnet_serialize_array_exit(sc->serializes, sc->nserialize, slz);
416226634ef8SSepherosa Ziehau }
416326634ef8SSepherosa Ziehau 
416426634ef8SSepherosa Ziehau static int
mxge_tryserialize(struct ifnet * ifp,enum ifnet_serialize slz)416526634ef8SSepherosa Ziehau mxge_tryserialize(struct ifnet *ifp, enum ifnet_serialize slz)
416626634ef8SSepherosa Ziehau {
416726634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
416826634ef8SSepherosa Ziehau 
416926634ef8SSepherosa Ziehau 	return ifnet_serialize_array_try(sc->serializes, sc->nserialize, slz);
417026634ef8SSepherosa Ziehau }
417126634ef8SSepherosa Ziehau 
417226634ef8SSepherosa Ziehau #ifdef INVARIANTS
417326634ef8SSepherosa Ziehau 
417426634ef8SSepherosa Ziehau static void
mxge_serialize_assert(struct ifnet * ifp,enum ifnet_serialize slz,boolean_t serialized)417526634ef8SSepherosa Ziehau mxge_serialize_assert(struct ifnet *ifp, enum ifnet_serialize slz,
417626634ef8SSepherosa Ziehau     boolean_t serialized)
417726634ef8SSepherosa Ziehau {
417826634ef8SSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
417926634ef8SSepherosa Ziehau 
418026634ef8SSepherosa Ziehau 	ifnet_serialize_array_assert(sc->serializes, sc->nserialize,
418126634ef8SSepherosa Ziehau 	    slz, serialized);
418226634ef8SSepherosa Ziehau }
418326634ef8SSepherosa Ziehau 
418426634ef8SSepherosa Ziehau #endif	/* INVARIANTS */
418526634ef8SSepherosa Ziehau 
41862276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
41872276707eSSepherosa Ziehau 
41882276707eSSepherosa Ziehau static void
mxge_npoll_rx(struct ifnet * ifp,void * xss,int cycle)41892276707eSSepherosa Ziehau mxge_npoll_rx(struct ifnet *ifp, void *xss, int cycle)
41902276707eSSepherosa Ziehau {
41912276707eSSepherosa Ziehau 	struct mxge_slice_state *ss = xss;
41922276707eSSepherosa Ziehau 	mxge_rx_done_t *rx_done = &ss->rx_data.rx_done;
41932276707eSSepherosa Ziehau 
41942276707eSSepherosa Ziehau 	ASSERT_SERIALIZED(&ss->rx_data.rx_serialize);
41952276707eSSepherosa Ziehau 
41962276707eSSepherosa Ziehau 	if (rx_done->entry[rx_done->idx].length != 0) {
41972276707eSSepherosa Ziehau 		mxge_clean_rx_done(&ss->sc->arpcom.ac_if, &ss->rx_data, cycle);
41982276707eSSepherosa Ziehau 	} else {
41992276707eSSepherosa Ziehau 		/*
42002276707eSSepherosa Ziehau 		 * XXX
42012276707eSSepherosa Ziehau 		 * This register writting obviously has cost,
42022276707eSSepherosa Ziehau 		 * however, if we don't hand back the rx token,
42032276707eSSepherosa Ziehau 		 * the upcoming packets may suffer rediculously
42042276707eSSepherosa Ziehau 		 * large delay, as observed on 8AL-C using ping(8).
42052276707eSSepherosa Ziehau 		 */
42062276707eSSepherosa Ziehau 		*ss->irq_claim = be32toh(3);
42072276707eSSepherosa Ziehau 	}
42082276707eSSepherosa Ziehau }
42092276707eSSepherosa Ziehau 
42102276707eSSepherosa Ziehau static void
mxge_npoll(struct ifnet * ifp,struct ifpoll_info * info)42112276707eSSepherosa Ziehau mxge_npoll(struct ifnet *ifp, struct ifpoll_info *info)
42122276707eSSepherosa Ziehau {
42132276707eSSepherosa Ziehau 	struct mxge_softc *sc = ifp->if_softc;
42142276707eSSepherosa Ziehau 	int i;
42152276707eSSepherosa Ziehau 
42162276707eSSepherosa Ziehau 	if (info == NULL)
42172276707eSSepherosa Ziehau 		return;
42182276707eSSepherosa Ziehau 
42192276707eSSepherosa Ziehau 	/*
42202276707eSSepherosa Ziehau 	 * Only poll rx; polling tx and status don't seem to work
42212276707eSSepherosa Ziehau 	 */
42222276707eSSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
42232276707eSSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4224bdbc20adSSepherosa Ziehau 		int cpu = ss->intr_cpuid;
42252276707eSSepherosa Ziehau 
4226bdbc20adSSepherosa Ziehau 		KKASSERT(cpu < netisr_ncpus);
4227bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].poll_func = mxge_npoll_rx;
4228bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].arg = ss;
4229bdbc20adSSepherosa Ziehau 		info->ifpi_rx[cpu].serializer = &ss->rx_data.rx_serialize;
42302276707eSSepherosa Ziehau 	}
42312276707eSSepherosa Ziehau }
42322276707eSSepherosa Ziehau 
42332276707eSSepherosa Ziehau #endif	/* IFPOLL_ENABLE */
42342276707eSSepherosa Ziehau 
42358892ea20SAggelos Economopoulos static int
mxge_attach(device_t dev)42368892ea20SAggelos Economopoulos mxge_attach(device_t dev)
42378892ea20SAggelos Economopoulos {
42388892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
4239137195a6SAggelos Economopoulos 	struct ifnet *ifp = &sc->arpcom.ac_if;
4240aca8f373SSepherosa Ziehau 	int err, rid, i;
42418892ea20SAggelos Economopoulos 
4242f0115d64SAggelos Economopoulos 	/*
42437cc92483SSepherosa Ziehau 	 * Avoid rewriting half the lines in this file to use
4244f0115d64SAggelos Economopoulos 	 * &sc->arpcom.ac_if instead
4245f0115d64SAggelos Economopoulos 	 */
4246f0115d64SAggelos Economopoulos 	sc->ifp = ifp;
42478892ea20SAggelos Economopoulos 	sc->dev = dev;
42487cc92483SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
424900f2de12SSepherosa Ziehau 
425000f2de12SSepherosa Ziehau 	/* IFM_ETH_FORCEPAUSE can't be changed */
425100f2de12SSepherosa Ziehau 	ifmedia_init(&sc->media, IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE,
425200f2de12SSepherosa Ziehau 	    mxge_media_change, mxge_media_status);
42537cc92483SSepherosa Ziehau 
425426634ef8SSepherosa Ziehau 	lwkt_serialize_init(&sc->main_serialize);
425526634ef8SSepherosa Ziehau 
42568892ea20SAggelos Economopoulos 	mxge_fetch_tunables(sc);
42578892ea20SAggelos Economopoulos 
42588892ea20SAggelos Economopoulos 	err = bus_dma_tag_create(NULL,			/* parent */
42598892ea20SAggelos Economopoulos 				 1,			/* alignment */
42608892ea20SAggelos Economopoulos 				 0,			/* boundary */
42618892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* low */
42628892ea20SAggelos Economopoulos 				 BUS_SPACE_MAXADDR,	/* high */
42637cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
42647cc92483SSepherosa Ziehau 				 0, 			/* num segs */
42657cc92483SSepherosa Ziehau 				 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
42668892ea20SAggelos Economopoulos 				 0,			/* flags */
42678892ea20SAggelos Economopoulos 				 &sc->parent_dmat);	/* tag */
42688892ea20SAggelos Economopoulos 	if (err != 0) {
4269798c3369SSepherosa Ziehau 		device_printf(dev, "Err %d allocating parent dmat\n", err);
4270798c3369SSepherosa Ziehau 		goto failed;
42718892ea20SAggelos Economopoulos 	}
42728892ea20SAggelos Economopoulos 
4273e3dc37faSAggelos Economopoulos 	callout_init_mp(&sc->co_hdl);
42748892ea20SAggelos Economopoulos 
42758892ea20SAggelos Economopoulos 	mxge_setup_cfg_space(sc);
42768892ea20SAggelos Economopoulos 
42777cc92483SSepherosa Ziehau 	/*
42787cc92483SSepherosa Ziehau 	 * Map the board into the kernel
42797cc92483SSepherosa Ziehau 	 */
42808892ea20SAggelos Economopoulos 	rid = PCIR_BARS;
42817cc92483SSepherosa Ziehau 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
42827cc92483SSepherosa Ziehau 	    &rid, RF_ACTIVE);
42838892ea20SAggelos Economopoulos 	if (sc->mem_res == NULL) {
42848892ea20SAggelos Economopoulos 		device_printf(dev, "could not map memory\n");
42858892ea20SAggelos Economopoulos 		err = ENXIO;
4286798c3369SSepherosa Ziehau 		goto failed;
42878892ea20SAggelos Economopoulos 	}
42887cc92483SSepherosa Ziehau 
42898892ea20SAggelos Economopoulos 	sc->sram = rman_get_virtual(sc->mem_res);
42908892ea20SAggelos Economopoulos 	sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100;
42918892ea20SAggelos Economopoulos 	if (sc->sram_size > rman_get_size(sc->mem_res)) {
42928892ea20SAggelos Economopoulos 		device_printf(dev, "impossible memory region size %ld\n",
42938892ea20SAggelos Economopoulos 		    rman_get_size(sc->mem_res));
42948892ea20SAggelos Economopoulos 		err = ENXIO;
4295798c3369SSepherosa Ziehau 		goto failed;
42968892ea20SAggelos Economopoulos 	}
42978892ea20SAggelos Economopoulos 
42987cc92483SSepherosa Ziehau 	/*
42997cc92483SSepherosa Ziehau 	 * Make NULL terminated copy of the EEPROM strings section of
43007cc92483SSepherosa Ziehau 	 * lanai SRAM
43017cc92483SSepherosa Ziehau 	 */
43028892ea20SAggelos Economopoulos 	bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE);
43038892ea20SAggelos Economopoulos 	bus_space_read_region_1(rman_get_bustag(sc->mem_res),
43048892ea20SAggelos Economopoulos 	    rman_get_bushandle(sc->mem_res),
43058892ea20SAggelos Economopoulos 	    sc->sram_size - MXGE_EEPROM_STRINGS_SIZE,
43067cc92483SSepherosa Ziehau 	    sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE - 2);
43078892ea20SAggelos Economopoulos 	err = mxge_parse_strings(sc);
4308798c3369SSepherosa Ziehau 	if (err != 0) {
4309798c3369SSepherosa Ziehau 		device_printf(dev, "parse EEPROM string failed\n");
4310798c3369SSepherosa Ziehau 		goto failed;
4311798c3369SSepherosa Ziehau 	}
43128892ea20SAggelos Economopoulos 
43137cc92483SSepherosa Ziehau 	/*
43147cc92483SSepherosa Ziehau 	 * Enable write combining for efficient use of PCIe bus
43157cc92483SSepherosa Ziehau 	 */
43168892ea20SAggelos Economopoulos 	mxge_enable_wc(sc);
43178892ea20SAggelos Economopoulos 
43187cc92483SSepherosa Ziehau 	/*
43197cc92483SSepherosa Ziehau 	 * Allocate the out of band DMA memory
43207cc92483SSepherosa Ziehau 	 */
43217cc92483SSepherosa Ziehau 	err = mxge_dma_alloc(sc, &sc->cmd_dma, sizeof(mxge_cmd_t), 64);
4322798c3369SSepherosa Ziehau 	if (err != 0) {
4323798c3369SSepherosa Ziehau 		device_printf(dev, "alloc cmd DMA buf failed\n");
4324798c3369SSepherosa Ziehau 		goto failed;
4325798c3369SSepherosa Ziehau 	}
43267cc92483SSepherosa Ziehau 	sc->cmd = sc->cmd_dma.dmem_addr;
43277cc92483SSepherosa Ziehau 
43288892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64);
4329798c3369SSepherosa Ziehau 	if (err != 0) {
4330798c3369SSepherosa Ziehau 		device_printf(dev, "alloc zeropad DMA buf failed\n");
4331798c3369SSepherosa Ziehau 		goto failed;
4332798c3369SSepherosa Ziehau 	}
43338892ea20SAggelos Economopoulos 
43348892ea20SAggelos Economopoulos 	err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096);
4335798c3369SSepherosa Ziehau 	if (err != 0) {
4336798c3369SSepherosa Ziehau 		device_printf(dev, "alloc dmabench DMA buf failed\n");
4337798c3369SSepherosa Ziehau 		goto failed;
4338798c3369SSepherosa Ziehau 	}
43398892ea20SAggelos Economopoulos 
43407cc92483SSepherosa Ziehau 	/* Select & load the firmware */
43418892ea20SAggelos Economopoulos 	err = mxge_select_firmware(sc);
4342798c3369SSepherosa Ziehau 	if (err != 0) {
4343798c3369SSepherosa Ziehau 		device_printf(dev, "select firmware failed\n");
4344798c3369SSepherosa Ziehau 		goto failed;
4345798c3369SSepherosa Ziehau 	}
43468892ea20SAggelos Economopoulos 
43478892ea20SAggelos Economopoulos 	mxge_slice_probe(sc);
43488892ea20SAggelos Economopoulos 	err = mxge_alloc_slices(sc);
4349798c3369SSepherosa Ziehau 	if (err != 0) {
4350798c3369SSepherosa Ziehau 		device_printf(dev, "alloc slices failed\n");
4351798c3369SSepherosa Ziehau 		goto failed;
4352798c3369SSepherosa Ziehau 	}
43538892ea20SAggelos Economopoulos 
4354e6c7b753SSepherosa Ziehau 	err = mxge_alloc_intr(sc);
4355e6c7b753SSepherosa Ziehau 	if (err != 0) {
4356e6c7b753SSepherosa Ziehau 		device_printf(dev, "alloc intr failed\n");
4357e6c7b753SSepherosa Ziehau 		goto failed;
4358e6c7b753SSepherosa Ziehau 	}
4359e6c7b753SSepherosa Ziehau 
436026634ef8SSepherosa Ziehau 	/* Setup serializes */
436126634ef8SSepherosa Ziehau 	mxge_setup_serialize(sc);
436226634ef8SSepherosa Ziehau 
43638892ea20SAggelos Economopoulos 	err = mxge_reset(sc, 0);
4364798c3369SSepherosa Ziehau 	if (err != 0) {
4365798c3369SSepherosa Ziehau 		device_printf(dev, "reset failed\n");
4366798c3369SSepherosa Ziehau 		goto failed;
4367798c3369SSepherosa Ziehau 	}
43688892ea20SAggelos Economopoulos 
43698892ea20SAggelos Economopoulos 	err = mxge_alloc_rings(sc);
43708892ea20SAggelos Economopoulos 	if (err != 0) {
4371798c3369SSepherosa Ziehau 		device_printf(dev, "failed to allocate rings\n");
4372798c3369SSepherosa Ziehau 		goto failed;
43738892ea20SAggelos Economopoulos 	}
43748892ea20SAggelos Economopoulos 
43758892ea20SAggelos Economopoulos 	ifp->if_baudrate = IF_Gbps(10UL);
437689d55360SSepherosa Ziehau 	ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO;
43778892ea20SAggelos Economopoulos 	ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
437889d55360SSepherosa Ziehau 
437989d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_MTU;
438089d55360SSepherosa Ziehau #if 0
438189d55360SSepherosa Ziehau 	/* Well, its software, sigh */
438289d55360SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
438389d55360SSepherosa Ziehau #endif
43848892ea20SAggelos Economopoulos 	ifp->if_capenable = ifp->if_capabilities;
438589d55360SSepherosa Ziehau 
43868892ea20SAggelos Economopoulos 	ifp->if_softc = sc;
43878892ea20SAggelos Economopoulos 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
438889d55360SSepherosa Ziehau 	ifp->if_init = mxge_init;
43898892ea20SAggelos Economopoulos 	ifp->if_ioctl = mxge_ioctl;
43908892ea20SAggelos Economopoulos 	ifp->if_start = mxge_start;
43912276707eSSepherosa Ziehau #ifdef IFPOLL_ENABLE
43922276707eSSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_LEGACY)
43932276707eSSepherosa Ziehau 		ifp->if_npoll = mxge_npoll;
43942276707eSSepherosa Ziehau #endif
439526634ef8SSepherosa Ziehau 	ifp->if_serialize = mxge_serialize;
439626634ef8SSepherosa Ziehau 	ifp->if_deserialize = mxge_deserialize;
439726634ef8SSepherosa Ziehau 	ifp->if_tryserialize = mxge_tryserialize;
439826634ef8SSepherosa Ziehau #ifdef INVARIANTS
439926634ef8SSepherosa Ziehau 	ifp->if_serialize_assert = mxge_serialize_assert;
440026634ef8SSepherosa Ziehau #endif
440189d55360SSepherosa Ziehau 
4402820e213fSSepherosa Ziehau 	/* Increase TSO burst length */
4403820e213fSSepherosa Ziehau 	ifp->if_tsolen = (32 * ETHERMTU);
4404820e213fSSepherosa Ziehau 
44058892ea20SAggelos Economopoulos 	/* Initialise the ifmedia structure */
440689d55360SSepherosa Ziehau 	mxge_media_init(sc);
44078892ea20SAggelos Economopoulos 	mxge_media_probe(sc);
440889d55360SSepherosa Ziehau 
4409cf774bceSAggelos Economopoulos 	ether_ifattach(ifp, sc->mac_addr, NULL);
441089d55360SSepherosa Ziehau 
4411aca8f373SSepherosa Ziehau 	/* Setup TX rings and subqueues */
4412aca8f373SSepherosa Ziehau 	for (i = 0; i < sc->num_tx_rings; ++i) {
4413aca8f373SSepherosa Ziehau 		struct ifaltq_subque *ifsq = ifq_get_subq(&ifp->if_snd, i);
4414aca8f373SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4415aca8f373SSepherosa Ziehau 
4416aca8f373SSepherosa Ziehau 		ifsq_set_cpuid(ifsq, ss->intr_cpuid);
4417aca8f373SSepherosa Ziehau 		ifsq_set_hw_serialize(ifsq, &ss->tx.tx_serialize);
4418aca8f373SSepherosa Ziehau 		ifsq_set_priv(ifsq, &ss->tx);
4419aca8f373SSepherosa Ziehau 		ss->tx.ifsq = ifsq;
4420aca8f373SSepherosa Ziehau 
4421*e2292763SMatthew Dillon 		ifsq_watchdog_init(&ss->tx.watchdog, ifsq, mxge_watchdog, 0);
4422aca8f373SSepherosa Ziehau 	}
4423aca8f373SSepherosa Ziehau 
4424b9a8961fSSepherosa Ziehau 	/*
4425b9a8961fSSepherosa Ziehau 	 * XXX
4426b9a8961fSSepherosa Ziehau 	 * We are not ready to do "gather" jumbo frame, so
4427b9a8961fSSepherosa Ziehau 	 * limit MTU to MJUMPAGESIZE
4428b9a8961fSSepherosa Ziehau 	 */
4429b9a8961fSSepherosa Ziehau 	sc->max_mtu = MJUMPAGESIZE -
4430b9a8961fSSepherosa Ziehau 	    ETHER_HDR_LEN - EVL_ENCAPLEN - MXGEFW_PAD - 1;
443189d55360SSepherosa Ziehau 	sc->dying = 0;
443289d55360SSepherosa Ziehau 
4433e6c7b753SSepherosa Ziehau 	err = mxge_setup_intr(sc);
4434369c353eSAggelos Economopoulos 	if (err != 0) {
4435798c3369SSepherosa Ziehau 		device_printf(dev, "alloc and setup intr failed\n");
4436798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
4437798c3369SSepherosa Ziehau 		goto failed;
4438369c353eSAggelos Economopoulos 	}
443926634ef8SSepherosa Ziehau 
44408892ea20SAggelos Economopoulos 	mxge_add_sysctls(sc);
444189d55360SSepherosa Ziehau 
444214929979SSepherosa Ziehau 	/* Increase non-cluster mbuf limit; used by small RX rings */
444314929979SSepherosa Ziehau 	mb_inclimit(ifp->if_nmbclusters);
444414929979SSepherosa Ziehau 
4445c9317e74SSepherosa Ziehau 	callout_reset_bycpu(&sc->co_hdl, mxge_ticks, mxge_tick, sc,
4446e6c7b753SSepherosa Ziehau 	    sc->ss[0].intr_cpuid);
44478892ea20SAggelos Economopoulos 	return 0;
44488892ea20SAggelos Economopoulos 
4449798c3369SSepherosa Ziehau failed:
4450798c3369SSepherosa Ziehau 	mxge_detach(dev);
44518892ea20SAggelos Economopoulos 	return err;
44528892ea20SAggelos Economopoulos }
44538892ea20SAggelos Economopoulos 
44548892ea20SAggelos Economopoulos static int
mxge_detach(device_t dev)44558892ea20SAggelos Economopoulos mxge_detach(device_t dev)
44568892ea20SAggelos Economopoulos {
44578892ea20SAggelos Economopoulos 	mxge_softc_t *sc = device_get_softc(dev);
44588892ea20SAggelos Economopoulos 
4459798c3369SSepherosa Ziehau 	if (device_is_attached(dev)) {
4460798c3369SSepherosa Ziehau 		struct ifnet *ifp = sc->ifp;
446114929979SSepherosa Ziehau 		int mblimit = ifp->if_nmbclusters;
4462798c3369SSepherosa Ziehau 
446326634ef8SSepherosa Ziehau 		ifnet_serialize_all(ifp);
4464798c3369SSepherosa Ziehau 
44658892ea20SAggelos Economopoulos 		sc->dying = 1;
4466798c3369SSepherosa Ziehau 		if (ifp->if_flags & IFF_RUNNING)
446789d55360SSepherosa Ziehau 			mxge_close(sc, 1);
4468e3dc37faSAggelos Economopoulos 		callout_stop(&sc->co_hdl);
4469798c3369SSepherosa Ziehau 
4470e6c7b753SSepherosa Ziehau 		mxge_teardown_intr(sc, sc->num_slices);
4471798c3369SSepherosa Ziehau 
447226634ef8SSepherosa Ziehau 		ifnet_deserialize_all(ifp);
4473e3dc37faSAggelos Economopoulos 
447489d55360SSepherosa Ziehau 		callout_terminate(&sc->co_hdl);
447589d55360SSepherosa Ziehau 
4476798c3369SSepherosa Ziehau 		ether_ifdetach(ifp);
447714929979SSepherosa Ziehau 
447814929979SSepherosa Ziehau 		/* Decrease non-cluster mbuf limit increased by us */
447914929979SSepherosa Ziehau 		mb_inclimit(-mblimit);
4480798c3369SSepherosa Ziehau 	}
44818892ea20SAggelos Economopoulos 	ifmedia_removeall(&sc->media);
4482798c3369SSepherosa Ziehau 
4483798c3369SSepherosa Ziehau 	if (sc->cmd != NULL && sc->zeropad_dma.dmem_addr != NULL &&
4484798c3369SSepherosa Ziehau 	    sc->sram != NULL)
44858892ea20SAggelos Economopoulos 		mxge_dummy_rdma(sc, 0);
4486798c3369SSepherosa Ziehau 
4487e6c7b753SSepherosa Ziehau 	mxge_free_intr(sc);
44888892ea20SAggelos Economopoulos 	mxge_rem_sysctls(sc);
44898892ea20SAggelos Economopoulos 	mxge_free_rings(sc);
4490798c3369SSepherosa Ziehau 
4491e6c7b753SSepherosa Ziehau 	/* MUST after sysctls, intr and rings are freed */
44928892ea20SAggelos Economopoulos 	mxge_free_slices(sc);
4493798c3369SSepherosa Ziehau 
4494798c3369SSepherosa Ziehau 	if (sc->dmabench_dma.dmem_addr != NULL)
44958892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->dmabench_dma);
4496798c3369SSepherosa Ziehau 	if (sc->zeropad_dma.dmem_addr != NULL)
44978892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->zeropad_dma);
4498798c3369SSepherosa Ziehau 	if (sc->cmd_dma.dmem_addr != NULL)
44998892ea20SAggelos Economopoulos 		mxge_dma_free(&sc->cmd_dma);
4500798c3369SSepherosa Ziehau 
4501e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res != NULL) {
4502e6c7b753SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(2),
4503e6c7b753SSepherosa Ziehau 		    sc->msix_table_res);
4504798c3369SSepherosa Ziehau 	}
4505798c3369SSepherosa Ziehau 	if (sc->mem_res != NULL) {
4506798c3369SSepherosa Ziehau 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS,
4507798c3369SSepherosa Ziehau 		    sc->mem_res);
4508798c3369SSepherosa Ziehau 	}
4509798c3369SSepherosa Ziehau 
4510798c3369SSepherosa Ziehau 	if (sc->parent_dmat != NULL)
45118892ea20SAggelos Economopoulos 		bus_dma_tag_destroy(sc->parent_dmat);
4512798c3369SSepherosa Ziehau 
4513bdbc20adSSepherosa Ziehau 	if (sc->ring_map != NULL)
4514bdbc20adSSepherosa Ziehau 		if_ringmap_free(sc->ring_map);
4515bdbc20adSSepherosa Ziehau 
45168892ea20SAggelos Economopoulos 	return 0;
45178892ea20SAggelos Economopoulos }
45188892ea20SAggelos Economopoulos 
45198892ea20SAggelos Economopoulos static int
mxge_shutdown(device_t dev)45208892ea20SAggelos Economopoulos mxge_shutdown(device_t dev)
45218892ea20SAggelos Economopoulos {
45228892ea20SAggelos Economopoulos 	return 0;
45238892ea20SAggelos Economopoulos }
4524e6c7b753SSepherosa Ziehau 
4525e6c7b753SSepherosa Ziehau static void
mxge_free_msix(struct mxge_softc * sc,boolean_t setup)4526e6c7b753SSepherosa Ziehau mxge_free_msix(struct mxge_softc *sc, boolean_t setup)
4527e6c7b753SSepherosa Ziehau {
4528e6c7b753SSepherosa Ziehau 	int i;
4529e6c7b753SSepherosa Ziehau 
4530e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4531e6c7b753SSepherosa Ziehau 
4532e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4533e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4534e6c7b753SSepherosa Ziehau 
4535e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4536e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4537e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4538e6c7b753SSepherosa Ziehau 		}
4539e6c7b753SSepherosa Ziehau 		if (ss->intr_rid >= 0)
4540e6c7b753SSepherosa Ziehau 			pci_release_msix_vector(sc->dev, ss->intr_rid);
4541e6c7b753SSepherosa Ziehau 	}
4542e6c7b753SSepherosa Ziehau 	if (setup)
4543e6c7b753SSepherosa Ziehau 		pci_teardown_msix(sc->dev);
4544e6c7b753SSepherosa Ziehau }
4545e6c7b753SSepherosa Ziehau 
4546e6c7b753SSepherosa Ziehau static int
mxge_alloc_msix(struct mxge_softc * sc)4547e6c7b753SSepherosa Ziehau mxge_alloc_msix(struct mxge_softc *sc)
4548e6c7b753SSepherosa Ziehau {
4549e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4550bdbc20adSSepherosa Ziehau 	int rid, error, i;
4551e6c7b753SSepherosa Ziehau 	boolean_t setup = FALSE;
4552e6c7b753SSepherosa Ziehau 
4553e6c7b753SSepherosa Ziehau 	KKASSERT(sc->num_slices > 1);
4554e6c7b753SSepherosa Ziehau 
4555e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4556e6c7b753SSepherosa Ziehau 
4557e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4558e6c7b753SSepherosa Ziehau 	ss->intr_func = mxge_msi;
4559e6c7b753SSepherosa Ziehau 	ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4560e6c7b753SSepherosa Ziehau 	    "%s comb", device_get_nameunit(sc->dev));
4561e6c7b753SSepherosa Ziehau 	ss->intr_desc = ss->intr_desc0;
4562bdbc20adSSepherosa Ziehau 	ss->intr_cpuid = if_ringmap_cpumap(sc->ring_map, 0);
4563e6c7b753SSepherosa Ziehau 
4564e6c7b753SSepherosa Ziehau 	for (i = 1; i < sc->num_slices; ++i) {
4565e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4566e6c7b753SSepherosa Ziehau 
4567e6c7b753SSepherosa Ziehau 		ss->intr_serialize = &ss->rx_data.rx_serialize;
4568aca8f373SSepherosa Ziehau 		if (sc->num_tx_rings == 1) {
4569e6c7b753SSepherosa Ziehau 			ss->intr_func = mxge_msix_rx;
4570e6c7b753SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4571bdbc20adSSepherosa Ziehau 			    "%s rx%d", device_get_nameunit(sc->dev), i);
4572aca8f373SSepherosa Ziehau 		} else {
4573aca8f373SSepherosa Ziehau 			ss->intr_func = mxge_msix_rxtx;
4574aca8f373SSepherosa Ziehau 			ksnprintf(ss->intr_desc0, sizeof(ss->intr_desc0),
4575bdbc20adSSepherosa Ziehau 			    "%s rxtx%d", device_get_nameunit(sc->dev), i);
4576aca8f373SSepherosa Ziehau 		}
4577e6c7b753SSepherosa Ziehau 		ss->intr_desc = ss->intr_desc0;
4578bdbc20adSSepherosa Ziehau 		ss->intr_cpuid = if_ringmap_cpumap(sc->ring_map, i);
4579e6c7b753SSepherosa Ziehau 	}
4580e6c7b753SSepherosa Ziehau 
4581e6c7b753SSepherosa Ziehau 	rid = PCIR_BAR(2);
4582e6c7b753SSepherosa Ziehau 	sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
4583e6c7b753SSepherosa Ziehau 	    &rid, RF_ACTIVE);
4584e6c7b753SSepherosa Ziehau 	if (sc->msix_table_res == NULL) {
4585e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "couldn't alloc MSI-X table res\n");
4586e6c7b753SSepherosa Ziehau 		return ENXIO;
4587e6c7b753SSepherosa Ziehau 	}
4588e6c7b753SSepherosa Ziehau 
4589e6c7b753SSepherosa Ziehau 	error = pci_setup_msix(sc->dev);
4590e6c7b753SSepherosa Ziehau 	if (error) {
4591e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not setup MSI-X\n");
4592e6c7b753SSepherosa Ziehau 		goto back;
4593e6c7b753SSepherosa Ziehau 	}
4594e6c7b753SSepherosa Ziehau 	setup = TRUE;
4595e6c7b753SSepherosa Ziehau 
4596e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4597e6c7b753SSepherosa Ziehau 		ss = &sc->ss[i];
4598e6c7b753SSepherosa Ziehau 
4599e6c7b753SSepherosa Ziehau 		error = pci_alloc_msix_vector(sc->dev, i, &ss->intr_rid,
4600e6c7b753SSepherosa Ziehau 		    ss->intr_cpuid);
4601e6c7b753SSepherosa Ziehau 		if (error) {
4602e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4603e6c7b753SSepherosa Ziehau 			    "MSI-X %d on cpu%d\n", i, ss->intr_cpuid);
4604e6c7b753SSepherosa Ziehau 			goto back;
4605e6c7b753SSepherosa Ziehau 		}
4606e6c7b753SSepherosa Ziehau 
4607e6c7b753SSepherosa Ziehau 		ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4608e6c7b753SSepherosa Ziehau 		    &ss->intr_rid, RF_ACTIVE);
4609e6c7b753SSepherosa Ziehau 		if (ss->intr_res == NULL) {
4610e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "could not alloc "
4611e6c7b753SSepherosa Ziehau 			    "MSI-X %d resource\n", i);
4612e6c7b753SSepherosa Ziehau 			error = ENXIO;
4613e6c7b753SSepherosa Ziehau 			goto back;
4614e6c7b753SSepherosa Ziehau 		}
4615e6c7b753SSepherosa Ziehau 	}
4616e6c7b753SSepherosa Ziehau 
4617e6c7b753SSepherosa Ziehau 	pci_enable_msix(sc->dev);
4618e6c7b753SSepherosa Ziehau 	sc->intr_type = PCI_INTR_TYPE_MSIX;
4619e6c7b753SSepherosa Ziehau back:
4620e6c7b753SSepherosa Ziehau 	if (error)
4621e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, setup);
4622e6c7b753SSepherosa Ziehau 	return error;
4623e6c7b753SSepherosa Ziehau }
4624e6c7b753SSepherosa Ziehau 
4625e6c7b753SSepherosa Ziehau static int
mxge_alloc_intr(struct mxge_softc * sc)4626e6c7b753SSepherosa Ziehau mxge_alloc_intr(struct mxge_softc *sc)
4627e6c7b753SSepherosa Ziehau {
4628e6c7b753SSepherosa Ziehau 	struct mxge_slice_state *ss;
4629e6c7b753SSepherosa Ziehau 	u_int irq_flags;
4630e6c7b753SSepherosa Ziehau 
4631e6c7b753SSepherosa Ziehau 	if (sc->num_slices > 1) {
4632e6c7b753SSepherosa Ziehau 		int error;
4633e6c7b753SSepherosa Ziehau 
4634e6c7b753SSepherosa Ziehau 		error = mxge_alloc_msix(sc);
4635e6c7b753SSepherosa Ziehau 		if (error)
4636e6c7b753SSepherosa Ziehau 			return error;
4637e6c7b753SSepherosa Ziehau 		KKASSERT(sc->intr_type == PCI_INTR_TYPE_MSIX);
4638e6c7b753SSepherosa Ziehau 		return 0;
4639e6c7b753SSepherosa Ziehau 	}
4640e6c7b753SSepherosa Ziehau 
4641e6c7b753SSepherosa Ziehau 	ss = &sc->ss[0];
4642e6c7b753SSepherosa Ziehau 
4643e6c7b753SSepherosa Ziehau 	sc->intr_type = pci_alloc_1intr(sc->dev, mxge_msi_enable,
4644e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, &irq_flags);
4645e6c7b753SSepherosa Ziehau 
4646e6c7b753SSepherosa Ziehau 	ss->intr_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
4647e6c7b753SSepherosa Ziehau 	    &ss->intr_rid, irq_flags);
4648e6c7b753SSepherosa Ziehau 	if (ss->intr_res == NULL) {
4649e6c7b753SSepherosa Ziehau 		device_printf(sc->dev, "could not alloc interrupt\n");
4650e6c7b753SSepherosa Ziehau 		return ENXIO;
4651e6c7b753SSepherosa Ziehau 	}
4652e6c7b753SSepherosa Ziehau 
4653e6c7b753SSepherosa Ziehau 	if (sc->intr_type == PCI_INTR_TYPE_LEGACY)
4654e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_legacy;
4655e6c7b753SSepherosa Ziehau 	else
4656e6c7b753SSepherosa Ziehau 		ss->intr_func = mxge_msi;
4657e6c7b753SSepherosa Ziehau 	ss->intr_serialize = &sc->main_serialize;
4658e6c7b753SSepherosa Ziehau 	ss->intr_cpuid = rman_get_cpuid(ss->intr_res);
4659e6c7b753SSepherosa Ziehau 
4660e6c7b753SSepherosa Ziehau 	return 0;
4661e6c7b753SSepherosa Ziehau }
4662e6c7b753SSepherosa Ziehau 
4663e6c7b753SSepherosa Ziehau static int
mxge_setup_intr(struct mxge_softc * sc)4664e6c7b753SSepherosa Ziehau mxge_setup_intr(struct mxge_softc *sc)
4665e6c7b753SSepherosa Ziehau {
4666e6c7b753SSepherosa Ziehau 	int i;
4667e6c7b753SSepherosa Ziehau 
4668e6c7b753SSepherosa Ziehau 	for (i = 0; i < sc->num_slices; ++i) {
4669e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4670e6c7b753SSepherosa Ziehau 		int error;
4671e6c7b753SSepherosa Ziehau 
4672e6c7b753SSepherosa Ziehau 		error = bus_setup_intr_descr(sc->dev, ss->intr_res,
4673e6c7b753SSepherosa Ziehau 		    INTR_MPSAFE, ss->intr_func, ss, &ss->intr_hand,
4674e6c7b753SSepherosa Ziehau 		    ss->intr_serialize, ss->intr_desc);
4675e6c7b753SSepherosa Ziehau 		if (error) {
4676e6c7b753SSepherosa Ziehau 			device_printf(sc->dev, "can't setup %dth intr\n", i);
4677e6c7b753SSepherosa Ziehau 			mxge_teardown_intr(sc, i);
4678e6c7b753SSepherosa Ziehau 			return error;
4679e6c7b753SSepherosa Ziehau 		}
4680e6c7b753SSepherosa Ziehau 	}
4681e6c7b753SSepherosa Ziehau 	return 0;
4682e6c7b753SSepherosa Ziehau }
4683e6c7b753SSepherosa Ziehau 
4684e6c7b753SSepherosa Ziehau static void
mxge_teardown_intr(struct mxge_softc * sc,int cnt)4685e6c7b753SSepherosa Ziehau mxge_teardown_intr(struct mxge_softc *sc, int cnt)
4686e6c7b753SSepherosa Ziehau {
4687e6c7b753SSepherosa Ziehau 	int i;
4688e6c7b753SSepherosa Ziehau 
4689e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4690e6c7b753SSepherosa Ziehau 		return;
4691e6c7b753SSepherosa Ziehau 
4692e6c7b753SSepherosa Ziehau 	for (i = 0; i < cnt; ++i) {
4693e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[i];
4694e6c7b753SSepherosa Ziehau 
4695e6c7b753SSepherosa Ziehau 		bus_teardown_intr(sc->dev, ss->intr_res, ss->intr_hand);
4696e6c7b753SSepherosa Ziehau 	}
4697e6c7b753SSepherosa Ziehau }
4698e6c7b753SSepherosa Ziehau 
4699e6c7b753SSepherosa Ziehau static void
mxge_free_intr(struct mxge_softc * sc)4700e6c7b753SSepherosa Ziehau mxge_free_intr(struct mxge_softc *sc)
4701e6c7b753SSepherosa Ziehau {
4702e6c7b753SSepherosa Ziehau 	if (sc->ss == NULL)
4703e6c7b753SSepherosa Ziehau 		return;
4704e6c7b753SSepherosa Ziehau 
4705e6c7b753SSepherosa Ziehau 	if (sc->intr_type != PCI_INTR_TYPE_MSIX) {
4706e6c7b753SSepherosa Ziehau 		struct mxge_slice_state *ss = &sc->ss[0];
4707e6c7b753SSepherosa Ziehau 
4708e6c7b753SSepherosa Ziehau 		if (ss->intr_res != NULL) {
4709e6c7b753SSepherosa Ziehau 			bus_release_resource(sc->dev, SYS_RES_IRQ,
4710e6c7b753SSepherosa Ziehau 			    ss->intr_rid, ss->intr_res);
4711e6c7b753SSepherosa Ziehau 		}
4712e6c7b753SSepherosa Ziehau 		if (sc->intr_type == PCI_INTR_TYPE_MSI)
4713e6c7b753SSepherosa Ziehau 			pci_release_msi(sc->dev);
4714e6c7b753SSepherosa Ziehau 	} else {
4715e6c7b753SSepherosa Ziehau 		mxge_free_msix(sc, TRUE);
4716e6c7b753SSepherosa Ziehau 	}
4717e6c7b753SSepherosa Ziehau }
4718