16d87a65dSAndrew Gallatin /****************************************************************************** 2718cf2ccSPedro F. Giffuni SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3b2fc195eSAndrew Gallatin 4cabc512fSAndrew Gallatin Copyright (c) 2006-2013, Myricom Inc. 5b2fc195eSAndrew Gallatin All rights reserved. 6b2fc195eSAndrew Gallatin 7b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without 8b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met: 9b2fc195eSAndrew Gallatin 10b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice, 11b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer. 12b2fc195eSAndrew Gallatin 13eb8e82f5SAndrew Gallatin 2. Neither the name of the Myricom Inc, nor the names of its 14b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 15b2fc195eSAndrew Gallatin this software without specific prior written permission. 16b2fc195eSAndrew Gallatin 17b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 28b2fc195eSAndrew Gallatin 29b2fc195eSAndrew Gallatin ***************************************************************************/ 30b2fc195eSAndrew Gallatin 31b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 32b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 33b2fc195eSAndrew Gallatin 34b2fc195eSAndrew Gallatin #include <sys/param.h> 35b2fc195eSAndrew Gallatin #include <sys/systm.h> 36b2fc195eSAndrew Gallatin #include <sys/linker.h> 37b2fc195eSAndrew Gallatin #include <sys/firmware.h> 38b2fc195eSAndrew Gallatin #include <sys/endian.h> 39b2fc195eSAndrew Gallatin #include <sys/sockio.h> 40b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 41b2fc195eSAndrew Gallatin #include <sys/malloc.h> 42b2fc195eSAndrew Gallatin #include <sys/kdb.h> 43b2fc195eSAndrew Gallatin #include <sys/kernel.h> 444e7f640dSJohn Baldwin #include <sys/lock.h> 45b2fc195eSAndrew Gallatin #include <sys/module.h> 46b2fc195eSAndrew Gallatin #include <sys/socket.h> 47b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 48b2fc195eSAndrew Gallatin #include <sys/sx.h> 4972c042dfSAndrew Gallatin #include <sys/taskqueue.h> 501dbf944aSXin LI #include <contrib/zlib/zlib.h> 511dbf944aSXin LI #include <dev/zlib/zcalloc.h> 52b2fc195eSAndrew Gallatin 53b2fc195eSAndrew Gallatin #include <net/if.h> 5476039bc8SGleb Smirnoff #include <net/if_var.h> 55b2fc195eSAndrew Gallatin #include <net/if_arp.h> 56b2fc195eSAndrew Gallatin #include <net/ethernet.h> 57b2fc195eSAndrew Gallatin #include <net/if_dl.h> 58b2fc195eSAndrew Gallatin #include <net/if_media.h> 59b2fc195eSAndrew Gallatin 60b2fc195eSAndrew Gallatin #include <net/bpf.h> 61b2fc195eSAndrew Gallatin 62b2fc195eSAndrew Gallatin #include <net/if_types.h> 63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h> 64b2fc195eSAndrew Gallatin 65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 66b2fc195eSAndrew Gallatin #include <netinet/in.h> 67b2fc195eSAndrew Gallatin #include <netinet/ip.h> 680a7a780eSAndrew Gallatin #include <netinet/ip6.h> 69aed8e389SAndrew Gallatin #include <netinet/tcp.h> 7026dd49c6SAndrew Gallatin #include <netinet/tcp_lro.h> 710a7a780eSAndrew Gallatin #include <netinet6/ip6_var.h> 72b2fc195eSAndrew Gallatin 73b2fc195eSAndrew Gallatin #include <machine/bus.h> 74053e637fSAndrew Gallatin #include <machine/in_cksum.h> 75b2fc195eSAndrew Gallatin #include <machine/resource.h> 76b2fc195eSAndrew Gallatin #include <sys/bus.h> 77b2fc195eSAndrew Gallatin #include <sys/rman.h> 781e413cf9SAndrew Gallatin #include <sys/smp.h> 79b2fc195eSAndrew Gallatin 80b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 81b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 82e749ef6bSAndrew Gallatin #include <dev/pci/pci_private.h> /* XXX for pci_cfg_restore */ 83b2fc195eSAndrew Gallatin 84b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 85b2fc195eSAndrew Gallatin #include <vm/pmap.h> 86b2fc195eSAndrew Gallatin 87c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64) 88c2c14a69SAndrew Gallatin #include <machine/specialreg.h> 89c2c14a69SAndrew Gallatin #endif 90c2c14a69SAndrew Gallatin 916d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 926d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 931e413cf9SAndrew Gallatin /*#define MXGE_FAKE_IFP*/ 946d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 95869c7348SAndrew Gallatin #ifdef IFNET_BUF_RING 96869c7348SAndrew Gallatin #include <sys/buf_ring.h> 97869c7348SAndrew Gallatin #endif 98b2fc195eSAndrew Gallatin 99eb6219e3SAndrew Gallatin #include "opt_inet.h" 1000a7a780eSAndrew Gallatin #include "opt_inet6.h" 101eb6219e3SAndrew Gallatin 102b2fc195eSAndrew Gallatin /* tunable params */ 1036d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 104d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0; 1056d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 1065e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 1076d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 1085e7d8541SAndrew Gallatin static int mxge_verbose = 0; 109dce01b9bSAndrew Gallatin static int mxge_ticks; 1101e413cf9SAndrew Gallatin static int mxge_max_slices = 1; 1115769c5efSAndrew Gallatin static int mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT; 1121e413cf9SAndrew Gallatin static int mxge_always_promisc = 0; 113f9453025SAndrew Gallatin static int mxge_initial_mtu = ETHERMTU_JUMBO; 11465c69066SAndrew Gallatin static int mxge_throttle = 0; 1156d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 1166d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 1171e413cf9SAndrew Gallatin static char *mxge_fw_rss_aligned = "mxge_rss_eth_z8e"; 1181e413cf9SAndrew Gallatin static char *mxge_fw_rss_unaligned = "mxge_rss_ethp_z8e"; 119b2fc195eSAndrew Gallatin 1206d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 1216d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 1226d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 1236d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1246d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 125b2fc195eSAndrew Gallatin 1266d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 127b2fc195eSAndrew Gallatin { 128b2fc195eSAndrew Gallatin /* Device interface */ 1296d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1306d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1316d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1326d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 13361bfd867SSofian Brabez 13461bfd867SSofian Brabez DEVMETHOD_END 135b2fc195eSAndrew Gallatin }; 136b2fc195eSAndrew Gallatin 1376d87a65dSAndrew Gallatin static driver_t mxge_driver = 138b2fc195eSAndrew Gallatin { 1396d87a65dSAndrew Gallatin "mxge", 1406d87a65dSAndrew Gallatin mxge_methods, 1416d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 142b2fc195eSAndrew Gallatin }; 143b2fc195eSAndrew Gallatin 1446d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 145b2fc195eSAndrew Gallatin 146b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1476d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1486d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 149f9ae0280SAndrew Gallatin MODULE_DEPEND(mxge, zlib, 1, 1, 1); 150b2fc195eSAndrew Gallatin 1511e413cf9SAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc, int adopt); 1528fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data); 153a393336bSAndrew Gallatin static int mxge_close(mxge_softc_t *sc, int down); 154276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc); 155276edd10SAndrew Gallatin static void mxge_tick(void *arg); 1568fe615baSAndrew Gallatin 157b2fc195eSAndrew Gallatin static int 1586d87a65dSAndrew Gallatin mxge_probe(device_t dev) 159b2fc195eSAndrew Gallatin { 16001638550SAndrew Gallatin int rev; 16101638550SAndrew Gallatin 16201638550SAndrew Gallatin 1636d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 164f1544498SAndrew Gallatin ((pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E) || 165f1544498SAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E_9))) { 16601638550SAndrew Gallatin rev = pci_get_revid(dev); 16701638550SAndrew Gallatin switch (rev) { 16801638550SAndrew Gallatin case MXGE_PCI_REV_Z8E: 169b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 17001638550SAndrew Gallatin break; 17101638550SAndrew Gallatin case MXGE_PCI_REV_Z8ES: 17201638550SAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8B"); 17301638550SAndrew Gallatin break; 17401638550SAndrew Gallatin default: 17501638550SAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8??"); 17601638550SAndrew Gallatin device_printf(dev, "Unrecognized rev %d NIC\n", 17701638550SAndrew Gallatin rev); 17801638550SAndrew Gallatin break; 17901638550SAndrew Gallatin } 180b2fc195eSAndrew Gallatin return 0; 181b2fc195eSAndrew Gallatin } 182b2fc195eSAndrew Gallatin return ENXIO; 183b2fc195eSAndrew Gallatin } 184b2fc195eSAndrew Gallatin 185b2fc195eSAndrew Gallatin static void 1866d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 187b2fc195eSAndrew Gallatin { 188f9ae0280SAndrew Gallatin #if defined(__i386) || defined(__amd64) 189b2fc195eSAndrew Gallatin vm_offset_t len; 19047c2e987SAndrew Gallatin int err; 191b2fc195eSAndrew Gallatin 1924d69a9d0SAndrew Gallatin sc->wc = 1; 193b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 194c2c14a69SAndrew Gallatin err = pmap_change_attr((vm_offset_t) sc->sram, 195c2c14a69SAndrew Gallatin len, PAT_WRITE_COMBINING); 19647c2e987SAndrew Gallatin if (err != 0) { 197c2c14a69SAndrew Gallatin device_printf(sc->dev, "pmap_change_attr failed, %d\n", 198c2c14a69SAndrew Gallatin err); 1994d69a9d0SAndrew Gallatin sc->wc = 0; 200b2fc195eSAndrew Gallatin } 201f9ae0280SAndrew Gallatin #endif 202b2fc195eSAndrew Gallatin } 203b2fc195eSAndrew Gallatin 204b2fc195eSAndrew Gallatin 205b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 206b2fc195eSAndrew Gallatin static void 2076d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 208b2fc195eSAndrew Gallatin int error) 209b2fc195eSAndrew Gallatin { 210b2fc195eSAndrew Gallatin if (error == 0) { 211b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 212b2fc195eSAndrew Gallatin } 213b2fc195eSAndrew Gallatin } 214b2fc195eSAndrew Gallatin 215b2fc195eSAndrew Gallatin static int 2166d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 217b2fc195eSAndrew Gallatin bus_size_t alignment) 218b2fc195eSAndrew Gallatin { 219b2fc195eSAndrew Gallatin int err; 220b2fc195eSAndrew Gallatin device_t dev = sc->dev; 2211e413cf9SAndrew Gallatin bus_size_t boundary, maxsegsize; 2221e413cf9SAndrew Gallatin 2231e413cf9SAndrew Gallatin if (bytes > 4096 && alignment == 4096) { 2241e413cf9SAndrew Gallatin boundary = 0; 2251e413cf9SAndrew Gallatin maxsegsize = bytes; 2261e413cf9SAndrew Gallatin } else { 2271e413cf9SAndrew Gallatin boundary = 4096; 2281e413cf9SAndrew Gallatin maxsegsize = 4096; 2291e413cf9SAndrew Gallatin } 230b2fc195eSAndrew Gallatin 231b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 232b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 233b2fc195eSAndrew Gallatin alignment, /* alignment */ 2341e413cf9SAndrew Gallatin boundary, /* boundary */ 235b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 236b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 237b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 238b2fc195eSAndrew Gallatin bytes, /* maxsize */ 239b2fc195eSAndrew Gallatin 1, /* num segs */ 2401e413cf9SAndrew Gallatin maxsegsize, /* maxsegsize */ 241b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 242b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 243b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 244b2fc195eSAndrew Gallatin if (err != 0) { 245b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 246b2fc195eSAndrew Gallatin return err; 247b2fc195eSAndrew Gallatin } 248b2fc195eSAndrew Gallatin 249b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 250b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 251b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 252b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 253b2fc195eSAndrew Gallatin if (err != 0) { 254b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 255b2fc195eSAndrew Gallatin goto abort_with_dmat; 256b2fc195eSAndrew Gallatin } 257b2fc195eSAndrew Gallatin 258b2fc195eSAndrew Gallatin /* load the memory */ 259b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2606d87a65dSAndrew Gallatin mxge_dmamap_callback, 261b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 262b2fc195eSAndrew Gallatin if (err != 0) { 263b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 264b2fc195eSAndrew Gallatin goto abort_with_mem; 265b2fc195eSAndrew Gallatin } 266b2fc195eSAndrew Gallatin return 0; 267b2fc195eSAndrew Gallatin 268b2fc195eSAndrew Gallatin abort_with_mem: 269b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 270b2fc195eSAndrew Gallatin abort_with_dmat: 271b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 272b2fc195eSAndrew Gallatin return err; 273b2fc195eSAndrew Gallatin } 274b2fc195eSAndrew Gallatin 275b2fc195eSAndrew Gallatin 276b2fc195eSAndrew Gallatin static void 2776d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 278b2fc195eSAndrew Gallatin { 279b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 280b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 281b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 282b2fc195eSAndrew Gallatin } 283b2fc195eSAndrew Gallatin 284b2fc195eSAndrew Gallatin /* 285b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 286b2fc195eSAndrew Gallatin * SN=x\0 287b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 288b2fc195eSAndrew Gallatin * PC=text\0 289b2fc195eSAndrew Gallatin */ 290b2fc195eSAndrew Gallatin 291b2fc195eSAndrew Gallatin static int 2926d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 293b2fc195eSAndrew Gallatin { 294dedbe836SAndrew Gallatin char *ptr; 295a4b233ddSAndrew Gallatin int i, found_mac, found_sn2; 296dedbe836SAndrew Gallatin char *endptr; 297b2fc195eSAndrew Gallatin 298b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 299b2fc195eSAndrew Gallatin found_mac = 0; 300a4b233ddSAndrew Gallatin found_sn2 = 0; 301dedbe836SAndrew Gallatin while (*ptr != '\0') { 302dedbe836SAndrew Gallatin if (strncmp(ptr, "MAC=", 4) == 0) { 303dedbe836SAndrew Gallatin ptr += 4; 304dedbe836SAndrew Gallatin for (i = 0;;) { 305dedbe836SAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, &endptr, 16); 306dedbe836SAndrew Gallatin if (endptr - ptr != 2) 307b2fc195eSAndrew Gallatin goto abort; 308dedbe836SAndrew Gallatin ptr = endptr; 309dedbe836SAndrew Gallatin if (++i == 6) 310dedbe836SAndrew Gallatin break; 311dedbe836SAndrew Gallatin if (*ptr++ != ':') 312dedbe836SAndrew Gallatin goto abort; 313b2fc195eSAndrew Gallatin } 314dedbe836SAndrew Gallatin found_mac = 1; 315dedbe836SAndrew Gallatin } else if (strncmp(ptr, "PC=", 3) == 0) { 3165e7d8541SAndrew Gallatin ptr += 3; 317dedbe836SAndrew Gallatin strlcpy(sc->product_code_string, ptr, 318dedbe836SAndrew Gallatin sizeof(sc->product_code_string)); 319dedbe836SAndrew Gallatin } else if (!found_sn2 && (strncmp(ptr, "SN=", 3) == 0)) { 3205e7d8541SAndrew Gallatin ptr += 3; 321dedbe836SAndrew Gallatin strlcpy(sc->serial_number_string, ptr, 322dedbe836SAndrew Gallatin sizeof(sc->serial_number_string)); 323dedbe836SAndrew Gallatin } else if (strncmp(ptr, "SN2=", 4) == 0) { 324a4b233ddSAndrew Gallatin /* SN2 takes precedence over SN */ 325a4b233ddSAndrew Gallatin ptr += 4; 326a4b233ddSAndrew Gallatin found_sn2 = 1; 327dedbe836SAndrew Gallatin strlcpy(sc->serial_number_string, ptr, 328dedbe836SAndrew Gallatin sizeof(sc->serial_number_string)); 329b2fc195eSAndrew Gallatin } 330dedbe836SAndrew Gallatin while (*ptr++ != '\0') {} 331b2fc195eSAndrew Gallatin } 332b2fc195eSAndrew Gallatin 333b2fc195eSAndrew Gallatin if (found_mac) 334b2fc195eSAndrew Gallatin return 0; 335b2fc195eSAndrew Gallatin 336b2fc195eSAndrew Gallatin abort: 337b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 338b2fc195eSAndrew Gallatin 339b2fc195eSAndrew Gallatin return ENXIO; 340b2fc195eSAndrew Gallatin } 341b2fc195eSAndrew Gallatin 3420d151103SRoman Divacky #if defined __i386 || defined i386 || defined __i386__ || defined __x86_64__ 3438fe615baSAndrew Gallatin static void 3448fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc) 345b2fc195eSAndrew Gallatin { 346b2fc195eSAndrew Gallatin uint32_t val; 3478fe615baSAndrew Gallatin unsigned long base, off; 348b2fc195eSAndrew Gallatin char *va, *cfgptr; 3498fe615baSAndrew Gallatin device_t pdev, mcp55; 3508fe615baSAndrew Gallatin uint16_t vendor_id, device_id, word; 351b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 352b2fc195eSAndrew Gallatin uint32_t *ptr32; 353b2fc195eSAndrew Gallatin 3548fe615baSAndrew Gallatin 3558fe615baSAndrew Gallatin if (!mxge_nvidia_ecrc_enable) 3568fe615baSAndrew Gallatin return; 3578fe615baSAndrew Gallatin 3588fe615baSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 3598fe615baSAndrew Gallatin if (pdev == NULL) { 3608fe615baSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 3618fe615baSAndrew Gallatin return; 3628fe615baSAndrew Gallatin } 3638fe615baSAndrew Gallatin vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2); 3648fe615baSAndrew Gallatin device_id = pci_read_config(pdev, PCIR_DEVICE, 2); 3658fe615baSAndrew Gallatin 3668fe615baSAndrew Gallatin if (vendor_id != 0x10de) 3678fe615baSAndrew Gallatin return; 3688fe615baSAndrew Gallatin 3698fe615baSAndrew Gallatin base = 0; 3708fe615baSAndrew Gallatin 3718fe615baSAndrew Gallatin if (device_id == 0x005d) { 3728fe615baSAndrew Gallatin /* ck804, base address is magic */ 3738fe615baSAndrew Gallatin base = 0xe0000000UL; 3748fe615baSAndrew Gallatin } else if (device_id >= 0x0374 && device_id <= 0x378) { 3758fe615baSAndrew Gallatin /* mcp55, base address stored in chipset */ 3768fe615baSAndrew Gallatin mcp55 = pci_find_bsf(0, 0, 0); 3778fe615baSAndrew Gallatin if (mcp55 && 3788fe615baSAndrew Gallatin 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) && 3798fe615baSAndrew Gallatin 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) { 3808fe615baSAndrew Gallatin word = pci_read_config(mcp55, 0x90, 2); 3818fe615baSAndrew Gallatin base = ((unsigned long)word & 0x7ffeU) << 25; 3828fe615baSAndrew Gallatin } 3838fe615baSAndrew Gallatin } 3848fe615baSAndrew Gallatin if (!base) 3858fe615baSAndrew Gallatin return; 3868fe615baSAndrew Gallatin 387b2fc195eSAndrew Gallatin /* XXXX 388b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 389b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 390b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 391b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 392b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 393b2fc195eSAndrew Gallatin */ 394b2fc195eSAndrew Gallatin #if 0 395b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 396b2fc195eSAndrew Gallatin config space */ 397b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 398b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 399b2fc195eSAndrew Gallatin val |= 0x40; 400b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 4018fe615baSAndrew Gallatin return; 402b2fc195eSAndrew Gallatin } 403b2fc195eSAndrew Gallatin #endif 404b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 405b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 406b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 407b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 408b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 409b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 410b2fc195eSAndrew Gallatin */ 411b2fc195eSAndrew Gallatin 412b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 413b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 414b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 415b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 416b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 417b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 418b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 419b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 420b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 421b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 422b2fc195eSAndrew Gallatin 4238fe615baSAndrew Gallatin off = base 424b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 425b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 426b2fc195eSAndrew Gallatin + 8 * slot); 427b2fc195eSAndrew Gallatin 428b2fc195eSAndrew Gallatin /* map it into the kernel */ 429b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 430b2fc195eSAndrew Gallatin 431b2fc195eSAndrew Gallatin 432b2fc195eSAndrew Gallatin if (va == NULL) { 433b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 4348fe615baSAndrew Gallatin return; 435b2fc195eSAndrew Gallatin } 436b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 437b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 438b2fc195eSAndrew Gallatin 439b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 440b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 441b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 442b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 443b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 444b2fc195eSAndrew Gallatin vendor_id, device_id); 445b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4468fe615baSAndrew Gallatin return; 447b2fc195eSAndrew Gallatin } 448b2fc195eSAndrew Gallatin 449b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 450b2fc195eSAndrew Gallatin val = *ptr32; 451b2fc195eSAndrew Gallatin 452b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 453b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 454b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4558fe615baSAndrew Gallatin return; 456b2fc195eSAndrew Gallatin } 457b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 458b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4595e7d8541SAndrew Gallatin if (mxge_verbose) 460b2fc195eSAndrew Gallatin device_printf(sc->dev, 4615e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 4625e7d8541SAndrew Gallatin "at %d:%d:%d\n", 463b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 4648fe615baSAndrew Gallatin return; 465b2fc195eSAndrew Gallatin } 466b2fc195eSAndrew Gallatin #else 4678fe615baSAndrew Gallatin static void 468f9ae0280SAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc) 469b2fc195eSAndrew Gallatin { 470b2fc195eSAndrew Gallatin device_printf(sc->dev, 471b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 4728fe615baSAndrew Gallatin return; 473b2fc195eSAndrew Gallatin } 474b2fc195eSAndrew Gallatin #endif 4758fe615baSAndrew Gallatin 4768fe615baSAndrew Gallatin 4778fe615baSAndrew Gallatin static int 4788fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type) 4798fe615baSAndrew Gallatin { 4808fe615baSAndrew Gallatin mxge_cmd_t cmd; 4818fe615baSAndrew Gallatin bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr; 4828fe615baSAndrew Gallatin int status; 4838fe615baSAndrew Gallatin uint32_t len; 4848fe615baSAndrew Gallatin char *test = " "; 4858fe615baSAndrew Gallatin 4868fe615baSAndrew Gallatin 4878fe615baSAndrew Gallatin /* Run a small DMA test. 4888fe615baSAndrew Gallatin * The magic multipliers to the length tell the firmware 4898fe615baSAndrew Gallatin * to do DMA read, write, or read+write tests. The 4908fe615baSAndrew Gallatin * results are returned in cmd.data0. The upper 16 4918fe615baSAndrew Gallatin * bits of the return is the number of transfers completed. 4928fe615baSAndrew Gallatin * The lower 16 bits is the time in 0.5us ticks that the 4938fe615baSAndrew Gallatin * transfers took to complete. 4948fe615baSAndrew Gallatin */ 4958fe615baSAndrew Gallatin 4961e413cf9SAndrew Gallatin len = sc->tx_boundary; 4978fe615baSAndrew Gallatin 4988fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4998fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 5008fe615baSAndrew Gallatin cmd.data2 = len * 0x10000; 5018fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 5028fe615baSAndrew Gallatin if (status != 0) { 5038fe615baSAndrew Gallatin test = "read"; 5048fe615baSAndrew Gallatin goto abort; 5058fe615baSAndrew Gallatin } 5068fe615baSAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * len * 2) / 5078fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 5088fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 5098fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 5108fe615baSAndrew Gallatin cmd.data2 = len * 0x1; 5118fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 5128fe615baSAndrew Gallatin if (status != 0) { 5138fe615baSAndrew Gallatin test = "write"; 5148fe615baSAndrew Gallatin goto abort; 5158fe615baSAndrew Gallatin } 5168fe615baSAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * len * 2) / 5178fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 5188fe615baSAndrew Gallatin 5198fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 5208fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 5218fe615baSAndrew Gallatin cmd.data2 = len * 0x10001; 5228fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 5238fe615baSAndrew Gallatin if (status != 0) { 5248fe615baSAndrew Gallatin test = "read/write"; 5258fe615baSAndrew Gallatin goto abort; 5268fe615baSAndrew Gallatin } 5278fe615baSAndrew Gallatin sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) / 5288fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 5298fe615baSAndrew Gallatin 5308fe615baSAndrew Gallatin abort: 5318fe615baSAndrew Gallatin if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) 5328fe615baSAndrew Gallatin device_printf(sc->dev, "DMA %s benchmark failed: %d\n", 5338fe615baSAndrew Gallatin test, status); 5348fe615baSAndrew Gallatin 5358fe615baSAndrew Gallatin return status; 5368fe615baSAndrew Gallatin } 5378fe615baSAndrew Gallatin 538b2fc195eSAndrew Gallatin /* 539b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 540b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 541b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 542b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 543b2fc195eSAndrew Gallatin * ECRC generation (if supported). 544b2fc195eSAndrew Gallatin * 545b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 546b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 547b2fc195eSAndrew Gallatin * 548b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 549b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 550b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 551b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 5521e413cf9SAndrew Gallatin * larger than 2KB by setting the tx_boundary to 2KB. If ECRC is 553b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 5541e413cf9SAndrew Gallatin * firmware image, and set tx_boundary to 4KB. 555b2fc195eSAndrew Gallatin */ 556b2fc195eSAndrew Gallatin 5578fe615baSAndrew Gallatin static int 5588fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc) 5598fe615baSAndrew Gallatin { 5608fe615baSAndrew Gallatin device_t dev = sc->dev; 5618fe615baSAndrew Gallatin int reg, status; 5628fe615baSAndrew Gallatin uint16_t pectl; 5638fe615baSAndrew Gallatin 5641e413cf9SAndrew Gallatin sc->tx_boundary = 4096; 5658fe615baSAndrew Gallatin /* 5668fe615baSAndrew Gallatin * Verify the max read request size was set to 4KB 5678fe615baSAndrew Gallatin * before trying the test with 4KB. 5688fe615baSAndrew Gallatin */ 5693b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { 5708fe615baSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 5718fe615baSAndrew Gallatin if ((pectl & (5 << 12)) != (5 << 12)) { 5728fe615baSAndrew Gallatin device_printf(dev, "Max Read Req. size != 4k (0x%x\n", 5738fe615baSAndrew Gallatin pectl); 5741e413cf9SAndrew Gallatin sc->tx_boundary = 2048; 5758fe615baSAndrew Gallatin } 5768fe615baSAndrew Gallatin } 5778fe615baSAndrew Gallatin 5788fe615baSAndrew Gallatin /* 5798fe615baSAndrew Gallatin * load the optimized firmware (which assumes aligned PCIe 5808fe615baSAndrew Gallatin * completions) in order to see if it works on this host. 5818fe615baSAndrew Gallatin */ 5828fe615baSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 5831e413cf9SAndrew Gallatin status = mxge_load_firmware(sc, 1); 5848fe615baSAndrew Gallatin if (status != 0) { 5858fe615baSAndrew Gallatin return status; 5868fe615baSAndrew Gallatin } 5878fe615baSAndrew Gallatin 5888fe615baSAndrew Gallatin /* 5898fe615baSAndrew Gallatin * Enable ECRC if possible 5908fe615baSAndrew Gallatin */ 5918fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(sc); 5928fe615baSAndrew Gallatin 5938fe615baSAndrew Gallatin /* 5948fe615baSAndrew Gallatin * Run a DMA test which watches for unaligned completions and 595a4b233ddSAndrew Gallatin * aborts on the first one seen. Not required on Z8ES or newer. 5968fe615baSAndrew Gallatin */ 597a4b233ddSAndrew Gallatin if (pci_get_revid(sc->dev) >= MXGE_PCI_REV_Z8ES) 598a4b233ddSAndrew Gallatin return 0; 5998fe615baSAndrew Gallatin status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST); 6008fe615baSAndrew Gallatin if (status == 0) 6018fe615baSAndrew Gallatin return 0; /* keep the aligned firmware */ 6028fe615baSAndrew Gallatin 6038fe615baSAndrew Gallatin if (status != E2BIG) 6048fe615baSAndrew Gallatin device_printf(dev, "DMA test failed: %d\n", status); 6058fe615baSAndrew Gallatin if (status == ENOSYS) 6068fe615baSAndrew Gallatin device_printf(dev, "Falling back to ethp! " 6078fe615baSAndrew Gallatin "Please install up to date fw\n"); 6088fe615baSAndrew Gallatin return status; 6098fe615baSAndrew Gallatin } 6108fe615baSAndrew Gallatin 6118fe615baSAndrew Gallatin static int 6126d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 613b2fc195eSAndrew Gallatin { 6148fe615baSAndrew Gallatin int aligned = 0; 61565c69066SAndrew Gallatin int force_firmware = mxge_force_firmware; 616b2fc195eSAndrew Gallatin 61765c69066SAndrew Gallatin if (sc->throttle) 61865c69066SAndrew Gallatin force_firmware = sc->throttle; 619d91b1b49SAndrew Gallatin 62065c69066SAndrew Gallatin if (force_firmware != 0) { 62165c69066SAndrew Gallatin if (force_firmware == 1) 622d91b1b49SAndrew Gallatin aligned = 1; 623d91b1b49SAndrew Gallatin else 624d91b1b49SAndrew Gallatin aligned = 0; 625d91b1b49SAndrew Gallatin if (mxge_verbose) 626d91b1b49SAndrew Gallatin device_printf(sc->dev, 627d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 628d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 629d91b1b49SAndrew Gallatin goto abort; 630d91b1b49SAndrew Gallatin } 631d91b1b49SAndrew Gallatin 632d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 633d91b1b49SAndrew Gallatin firmware and skip any checks */ 634d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 635d91b1b49SAndrew Gallatin device_printf(sc->dev, 636d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 637d91b1b49SAndrew Gallatin sc->link_width); 638d91b1b49SAndrew Gallatin aligned = 1; 639d91b1b49SAndrew Gallatin goto abort; 640d91b1b49SAndrew Gallatin } 641d91b1b49SAndrew Gallatin 6428fe615baSAndrew Gallatin if (0 == mxge_firmware_probe(sc)) 6438fe615baSAndrew Gallatin return 0; 644b2fc195eSAndrew Gallatin 645b2fc195eSAndrew Gallatin abort: 646b2fc195eSAndrew Gallatin if (aligned) { 6476d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 6481e413cf9SAndrew Gallatin sc->tx_boundary = 4096; 649b2fc195eSAndrew Gallatin } else { 6506d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 6511e413cf9SAndrew Gallatin sc->tx_boundary = 2048; 652b2fc195eSAndrew Gallatin } 6531e413cf9SAndrew Gallatin return (mxge_load_firmware(sc, 0)); 654b2fc195eSAndrew Gallatin } 655b2fc195eSAndrew Gallatin 6564da0d523SAndrew Gallatin static int 6574da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 6584da0d523SAndrew Gallatin { 659b824b7d8SAndrew Gallatin 6604da0d523SAndrew Gallatin 6614da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 6624da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 6634da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 6644da0d523SAndrew Gallatin return EIO; 6654da0d523SAndrew Gallatin } 6664da0d523SAndrew Gallatin 6674da0d523SAndrew Gallatin /* save firmware version for sysctl */ 668dedbe836SAndrew Gallatin strlcpy(sc->fw_version, hdr->version, sizeof(sc->fw_version)); 6694da0d523SAndrew Gallatin if (mxge_verbose) 6704da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 6714da0d523SAndrew Gallatin 672b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 673b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 6744da0d523SAndrew Gallatin 675b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 676b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 6774da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 6784da0d523SAndrew Gallatin sc->fw_version); 6794da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 6804da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 6814da0d523SAndrew Gallatin return EINVAL; 6824da0d523SAndrew Gallatin } 6834da0d523SAndrew Gallatin return 0; 6844da0d523SAndrew Gallatin 6854da0d523SAndrew Gallatin } 686b2fc195eSAndrew Gallatin 687b2fc195eSAndrew Gallatin static int 6886d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 689b2fc195eSAndrew Gallatin { 690f9ae0280SAndrew Gallatin z_stream zs; 691f9ae0280SAndrew Gallatin char *inflate_buffer; 69233d54970SLuigi Rizzo const struct firmware *fw; 693b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 694b2fc195eSAndrew Gallatin unsigned hdr_offset; 695b2fc195eSAndrew Gallatin int status; 6964da0d523SAndrew Gallatin unsigned int i; 6974da0d523SAndrew Gallatin char dummy; 698f9ae0280SAndrew Gallatin size_t fw_len; 699b2fc195eSAndrew Gallatin 700b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 701b2fc195eSAndrew Gallatin if (fw == NULL) { 702b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 703b2fc195eSAndrew Gallatin sc->fw_name); 704b2fc195eSAndrew Gallatin return ENOENT; 705b2fc195eSAndrew Gallatin } 706b2fc195eSAndrew Gallatin 707f9ae0280SAndrew Gallatin 708f9ae0280SAndrew Gallatin 709f9ae0280SAndrew Gallatin /* setup zlib and decompress f/w */ 710f9ae0280SAndrew Gallatin bzero(&zs, sizeof (zs)); 7111dbf944aSXin LI zs.zalloc = zcalloc_nowait; 7121dbf944aSXin LI zs.zfree = zcfree; 713f9ae0280SAndrew Gallatin status = inflateInit(&zs); 714f9ae0280SAndrew Gallatin if (status != Z_OK) { 715b2fc195eSAndrew Gallatin status = EIO; 716b2fc195eSAndrew Gallatin goto abort_with_fw; 717b2fc195eSAndrew Gallatin } 718f9ae0280SAndrew Gallatin 719f9ae0280SAndrew Gallatin /* the uncompressed size is stored as the firmware version, 720f9ae0280SAndrew Gallatin which would otherwise go unused */ 721f9ae0280SAndrew Gallatin fw_len = (size_t) fw->version; 722f9ae0280SAndrew Gallatin inflate_buffer = malloc(fw_len, M_TEMP, M_NOWAIT); 723f9ae0280SAndrew Gallatin if (inflate_buffer == NULL) 724f9ae0280SAndrew Gallatin goto abort_with_zs; 725f9ae0280SAndrew Gallatin zs.avail_in = fw->datasize; 726f9ae0280SAndrew Gallatin zs.next_in = __DECONST(char *, fw->data); 727f9ae0280SAndrew Gallatin zs.avail_out = fw_len; 728f9ae0280SAndrew Gallatin zs.next_out = inflate_buffer; 729f9ae0280SAndrew Gallatin status = inflate(&zs, Z_FINISH); 730f9ae0280SAndrew Gallatin if (status != Z_STREAM_END) { 731f9ae0280SAndrew Gallatin device_printf(sc->dev, "zlib %d\n", status); 732f9ae0280SAndrew Gallatin status = EIO; 733f9ae0280SAndrew Gallatin goto abort_with_buffer; 734f9ae0280SAndrew Gallatin } 735f9ae0280SAndrew Gallatin 736f9ae0280SAndrew Gallatin /* check id */ 737f9ae0280SAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 738f9ae0280SAndrew Gallatin (inflate_buffer + MCP_HEADER_PTR_OFFSET)); 739f9ae0280SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw_len) { 740f9ae0280SAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 741f9ae0280SAndrew Gallatin status = EIO; 742f9ae0280SAndrew Gallatin goto abort_with_buffer; 743f9ae0280SAndrew Gallatin } 744f9ae0280SAndrew Gallatin hdr = (const void*)(inflate_buffer + hdr_offset); 745b2fc195eSAndrew Gallatin 7464da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 7474da0d523SAndrew Gallatin if (status != 0) 748f9ae0280SAndrew Gallatin goto abort_with_buffer; 749b2fc195eSAndrew Gallatin 750b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 751f9ae0280SAndrew Gallatin for (i = 0; i < fw_len; i += 256) { 7524da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 753f9ae0280SAndrew Gallatin inflate_buffer + i, 754f9ae0280SAndrew Gallatin min(256U, (unsigned)(fw_len - i))); 75573c7c83fSAndrew Gallatin wmb(); 7564da0d523SAndrew Gallatin dummy = *sc->sram; 75773c7c83fSAndrew Gallatin wmb(); 7584da0d523SAndrew Gallatin } 759b2fc195eSAndrew Gallatin 760f9ae0280SAndrew Gallatin *limit = fw_len; 761b2fc195eSAndrew Gallatin status = 0; 762f9ae0280SAndrew Gallatin abort_with_buffer: 763f9ae0280SAndrew Gallatin free(inflate_buffer, M_TEMP); 764f9ae0280SAndrew Gallatin abort_with_zs: 765f9ae0280SAndrew Gallatin inflateEnd(&zs); 766b2fc195eSAndrew Gallatin abort_with_fw: 767b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 768b2fc195eSAndrew Gallatin return status; 769b2fc195eSAndrew Gallatin } 770b2fc195eSAndrew Gallatin 771b2fc195eSAndrew Gallatin /* 772b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 773b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 774b2fc195eSAndrew Gallatin */ 775b2fc195eSAndrew Gallatin 776b2fc195eSAndrew Gallatin static void 7776d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 778b2fc195eSAndrew Gallatin { 779b2fc195eSAndrew Gallatin char buf_bytes[72]; 780b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 781b2fc195eSAndrew Gallatin volatile char *submit; 782b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 783b2fc195eSAndrew Gallatin int i; 784b2fc195eSAndrew Gallatin 785b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 786b2fc195eSAndrew Gallatin 787b2fc195eSAndrew Gallatin /* clear confirmation addr */ 788b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 789b2fc195eSAndrew Gallatin *confirm = 0; 79073c7c83fSAndrew Gallatin wmb(); 791b2fc195eSAndrew Gallatin 792b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 793b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 794b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 795b2fc195eSAndrew Gallatin */ 796b2fc195eSAndrew Gallatin 7976d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7986d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 799b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 800b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 801b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 8026d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 8036d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 804b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 805b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 806b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 807b2fc195eSAndrew Gallatin 808b2fc195eSAndrew Gallatin 8090fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 810b2fc195eSAndrew Gallatin 8116d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 81273c7c83fSAndrew Gallatin wmb(); 813b2fc195eSAndrew Gallatin DELAY(1000); 81473c7c83fSAndrew Gallatin wmb(); 815b2fc195eSAndrew Gallatin i = 0; 816b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 817b2fc195eSAndrew Gallatin DELAY(1000); 818b2fc195eSAndrew Gallatin i++; 819b2fc195eSAndrew Gallatin } 820b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 821b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 822b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 823b2fc195eSAndrew Gallatin *confirm); 824b2fc195eSAndrew Gallatin } 825b2fc195eSAndrew Gallatin return; 826b2fc195eSAndrew Gallatin } 827b2fc195eSAndrew Gallatin 828b2fc195eSAndrew Gallatin static int 8296d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 830b2fc195eSAndrew Gallatin { 831b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 832b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 833b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 8340fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 835b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 836e0501fd0SAndrew Gallatin int err, sleep_total = 0; 837b2fc195eSAndrew Gallatin 838b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 839b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 840b2fc195eSAndrew Gallatin 841b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 842b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 843b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 844b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 8456d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 8466d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 847b2fc195eSAndrew Gallatin 848b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 849b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 850a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 851b2fc195eSAndrew Gallatin response->result = 0xffffffff; 85273c7c83fSAndrew Gallatin wmb(); 8536d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 854b2fc195eSAndrew Gallatin 8555e7d8541SAndrew Gallatin /* wait up to 20ms */ 856e0501fd0SAndrew Gallatin err = EAGAIN; 8575e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 858b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 859b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 86073c7c83fSAndrew Gallatin wmb(); 861e0501fd0SAndrew Gallatin switch (be32toh(response->result)) { 862e0501fd0SAndrew Gallatin case 0: 863b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 864e0501fd0SAndrew Gallatin err = 0; 865e0501fd0SAndrew Gallatin break; 866e0501fd0SAndrew Gallatin case 0xffffffff: 867e0501fd0SAndrew Gallatin DELAY(1000); 868e0501fd0SAndrew Gallatin break; 869e0501fd0SAndrew Gallatin case MXGEFW_CMD_UNKNOWN: 870e0501fd0SAndrew Gallatin err = ENOSYS; 871e0501fd0SAndrew Gallatin break; 872e0501fd0SAndrew Gallatin case MXGEFW_CMD_ERROR_UNALIGNED: 873e0501fd0SAndrew Gallatin err = E2BIG; 874e0501fd0SAndrew Gallatin break; 875c587e59fSAndrew Gallatin case MXGEFW_CMD_ERROR_BUSY: 876c587e59fSAndrew Gallatin err = EBUSY; 877c587e59fSAndrew Gallatin break; 878c406ad2eSAndrew Gallatin case MXGEFW_CMD_ERROR_I2C_ABSENT: 879c406ad2eSAndrew Gallatin err = ENXIO; 880c406ad2eSAndrew Gallatin break; 881e0501fd0SAndrew Gallatin default: 882b2fc195eSAndrew Gallatin device_printf(sc->dev, 8836d87a65dSAndrew Gallatin "mxge: command %d " 884b2fc195eSAndrew Gallatin "failed, result = %d\n", 885b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 886e0501fd0SAndrew Gallatin err = ENXIO; 887e0501fd0SAndrew Gallatin break; 888b2fc195eSAndrew Gallatin } 889e0501fd0SAndrew Gallatin if (err != EAGAIN) 890e0501fd0SAndrew Gallatin break; 891b2fc195eSAndrew Gallatin } 892e0501fd0SAndrew Gallatin if (err == EAGAIN) 8936d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 894b2fc195eSAndrew Gallatin "result = %d\n", 895b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 896e0501fd0SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 897e0501fd0SAndrew Gallatin return err; 898b2fc195eSAndrew Gallatin } 899b2fc195eSAndrew Gallatin 9004da0d523SAndrew Gallatin static int 9014da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 9024da0d523SAndrew Gallatin { 9034da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 9044da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 9054da0d523SAndrew Gallatin size_t hdr_offset; 9064da0d523SAndrew Gallatin int status; 9074da0d523SAndrew Gallatin 9084da0d523SAndrew Gallatin /* find running firmware header */ 9094da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 9104da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 9114da0d523SAndrew Gallatin 9124da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 9134da0d523SAndrew Gallatin device_printf(sc->dev, 9144da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 9154da0d523SAndrew Gallatin (int)hdr_offset); 9164da0d523SAndrew Gallatin return EIO; 9174da0d523SAndrew Gallatin } 9184da0d523SAndrew Gallatin 9194da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 9204da0d523SAndrew Gallatin * validate firmware */ 9214da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 9224da0d523SAndrew Gallatin if (hdr == NULL) { 9234da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 9244da0d523SAndrew Gallatin return ENOMEM; 9254da0d523SAndrew Gallatin } 9264da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 9274da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 9284da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 9294da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 9304da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 931b824b7d8SAndrew Gallatin 932b824b7d8SAndrew Gallatin /* 933b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 934b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 935b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 936b824b7d8SAndrew Gallatin */ 937b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 938b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 939b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 940b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 941b824b7d8SAndrew Gallatin "working around rx filter bug\n", 942b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 943b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 944b824b7d8SAndrew Gallatin } 945b824b7d8SAndrew Gallatin 9464da0d523SAndrew Gallatin return status; 9474da0d523SAndrew Gallatin } 9484da0d523SAndrew Gallatin 949b2fc195eSAndrew Gallatin 950b2fc195eSAndrew Gallatin static int 9511e413cf9SAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc, int adopt) 952b2fc195eSAndrew Gallatin { 953b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 954b2fc195eSAndrew Gallatin volatile char *submit; 955b2fc195eSAndrew Gallatin char buf_bytes[72]; 956b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 957b2fc195eSAndrew Gallatin int status, i; 958b2fc195eSAndrew Gallatin 959b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 960b2fc195eSAndrew Gallatin 961b2fc195eSAndrew Gallatin size = sc->sram_size; 9626d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 963b2fc195eSAndrew Gallatin if (status) { 9641e413cf9SAndrew Gallatin if (!adopt) 9651e413cf9SAndrew Gallatin return status; 9664da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 9674da0d523SAndrew Gallatin it is new enough */ 9684da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 9694da0d523SAndrew Gallatin if (status) { 9704da0d523SAndrew Gallatin device_printf(sc->dev, 9714da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 972b2fc195eSAndrew Gallatin return status; 973b2fc195eSAndrew Gallatin } 9744da0d523SAndrew Gallatin device_printf(sc->dev, 9754da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 9761e413cf9SAndrew Gallatin if (sc->tx_boundary == 4096) { 9774da0d523SAndrew Gallatin device_printf(sc->dev, 9784da0d523SAndrew Gallatin "Using firmware currently running on NIC" 9794da0d523SAndrew Gallatin ". For optimal\n"); 9804da0d523SAndrew Gallatin device_printf(sc->dev, 9814da0d523SAndrew Gallatin "performance consider loading optimized " 9824da0d523SAndrew Gallatin "firmware\n"); 9834da0d523SAndrew Gallatin } 984d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 9851e413cf9SAndrew Gallatin sc->tx_boundary = 2048; 986d91b1b49SAndrew Gallatin return 0; 9874da0d523SAndrew Gallatin } 988b2fc195eSAndrew Gallatin /* clear confirmation addr */ 989b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 990b2fc195eSAndrew Gallatin *confirm = 0; 99173c7c83fSAndrew Gallatin wmb(); 992b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 993b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 994b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 995b2fc195eSAndrew Gallatin */ 996b2fc195eSAndrew Gallatin 9976d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 9986d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 999b2fc195eSAndrew Gallatin 1000b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 1001b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 1002b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 1003b2fc195eSAndrew Gallatin 1004b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 1005b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 1006b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 1007b2fc195eSAndrew Gallatin */ 1008b2fc195eSAndrew Gallatin /* where the code starts*/ 10096d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 1010b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 1011b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 1012b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 1013b2fc195eSAndrew Gallatin 10140fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 10156d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 101673c7c83fSAndrew Gallatin wmb(); 1017b2fc195eSAndrew Gallatin DELAY(1000); 101873c7c83fSAndrew Gallatin wmb(); 1019b2fc195eSAndrew Gallatin i = 0; 1020b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 1021b2fc195eSAndrew Gallatin DELAY(1000*10); 1022b2fc195eSAndrew Gallatin i++; 1023b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 1024b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 1025b2fc195eSAndrew Gallatin } 1026b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 1027b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 1028b2fc195eSAndrew Gallatin confirm, *confirm); 1029b2fc195eSAndrew Gallatin 1030b2fc195eSAndrew Gallatin return ENXIO; 1031b2fc195eSAndrew Gallatin } 1032b2fc195eSAndrew Gallatin return 0; 1033b2fc195eSAndrew Gallatin } 1034b2fc195eSAndrew Gallatin 1035b2fc195eSAndrew Gallatin static int 10366d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 1037b2fc195eSAndrew Gallatin { 10386d87a65dSAndrew Gallatin mxge_cmd_t cmd; 1039b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 1040b2fc195eSAndrew Gallatin int status; 1041b2fc195eSAndrew Gallatin 1042b2fc195eSAndrew Gallatin 1043b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 1044b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 1045b2fc195eSAndrew Gallatin 1046b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 1047b2fc195eSAndrew Gallatin 10485e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 1049b2fc195eSAndrew Gallatin return status; 1050b2fc195eSAndrew Gallatin } 1051b2fc195eSAndrew Gallatin 1052b2fc195eSAndrew Gallatin static int 10536d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 1054b2fc195eSAndrew Gallatin { 10556d87a65dSAndrew Gallatin mxge_cmd_t cmd; 1056b2fc195eSAndrew Gallatin int status; 1057b2fc195eSAndrew Gallatin 1058b2fc195eSAndrew Gallatin if (pause) 10595e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 1060b2fc195eSAndrew Gallatin &cmd); 1061b2fc195eSAndrew Gallatin else 10625e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 1063b2fc195eSAndrew Gallatin &cmd); 1064b2fc195eSAndrew Gallatin 1065b2fc195eSAndrew Gallatin if (status) { 1066b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 1067b2fc195eSAndrew Gallatin return ENXIO; 1068b2fc195eSAndrew Gallatin } 1069b2fc195eSAndrew Gallatin sc->pause = pause; 1070b2fc195eSAndrew Gallatin return 0; 1071b2fc195eSAndrew Gallatin } 1072b2fc195eSAndrew Gallatin 1073b2fc195eSAndrew Gallatin static void 10746d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 1075b2fc195eSAndrew Gallatin { 10766d87a65dSAndrew Gallatin mxge_cmd_t cmd; 1077b2fc195eSAndrew Gallatin int status; 1078b2fc195eSAndrew Gallatin 10791e413cf9SAndrew Gallatin if (mxge_always_promisc) 10801e413cf9SAndrew Gallatin promisc = 1; 10811e413cf9SAndrew Gallatin 1082b2fc195eSAndrew Gallatin if (promisc) 10835e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 1084b2fc195eSAndrew Gallatin &cmd); 1085b2fc195eSAndrew Gallatin else 10865e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 1087b2fc195eSAndrew Gallatin &cmd); 1088b2fc195eSAndrew Gallatin 1089b2fc195eSAndrew Gallatin if (status) { 1090b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 1091b2fc195eSAndrew Gallatin } 1092b2fc195eSAndrew Gallatin } 1093b2fc195eSAndrew Gallatin 10942a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx { 10952a0f8518SGleb Smirnoff mxge_softc_t *sc; 10962a0f8518SGleb Smirnoff int error; 10972a0f8518SGleb Smirnoff }; 10982a0f8518SGleb Smirnoff 10992a0f8518SGleb Smirnoff static u_int 11002a0f8518SGleb Smirnoff mxge_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 11012a0f8518SGleb Smirnoff { 11022a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx *ctx = arg; 11032a0f8518SGleb Smirnoff mxge_cmd_t cmd; 11042a0f8518SGleb Smirnoff 11052a0f8518SGleb Smirnoff if (ctx->error != 0) 11062a0f8518SGleb Smirnoff return (0); 11072a0f8518SGleb Smirnoff bcopy(LLADDR(sdl), &cmd.data0, 4); 11082a0f8518SGleb Smirnoff bcopy(LLADDR(sdl) + 4, &cmd.data1, 2); 11092a0f8518SGleb Smirnoff cmd.data0 = htonl(cmd.data0); 11102a0f8518SGleb Smirnoff cmd.data1 = htonl(cmd.data1); 11112a0f8518SGleb Smirnoff 11122a0f8518SGleb Smirnoff ctx->error = mxge_send_cmd(ctx->sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 11132a0f8518SGleb Smirnoff 11142a0f8518SGleb Smirnoff return (1); 11152a0f8518SGleb Smirnoff } 11162a0f8518SGleb Smirnoff 11170fa7f681SAndrew Gallatin static void 11180fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 11190fa7f681SAndrew Gallatin { 11202a0f8518SGleb Smirnoff struct mxge_add_maddr_ctx ctx; 11210fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 11222a0f8518SGleb Smirnoff mxge_cmd_t cmd; 11230fa7f681SAndrew Gallatin int err; 11240fa7f681SAndrew Gallatin 11250fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 11260fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 11270fa7f681SAndrew Gallatin return; 11280fa7f681SAndrew Gallatin 11290fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 11300fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 11310fa7f681SAndrew Gallatin if (err != 0) { 11320fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 11330fa7f681SAndrew Gallatin " error status: %d\n", err); 11340fa7f681SAndrew Gallatin return; 11350fa7f681SAndrew Gallatin } 11360fa7f681SAndrew Gallatin 1137b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 1138b824b7d8SAndrew Gallatin return; 11390fa7f681SAndrew Gallatin 11400fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 11410fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 11420fa7f681SAndrew Gallatin return; 11430fa7f681SAndrew Gallatin 11440fa7f681SAndrew Gallatin /* Flush all the filters */ 11450fa7f681SAndrew Gallatin 11460fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 11470fa7f681SAndrew Gallatin if (err != 0) { 11480fa7f681SAndrew Gallatin device_printf(sc->dev, 11490fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 11500fa7f681SAndrew Gallatin ", error status: %d\n", err); 11510fa7f681SAndrew Gallatin return; 11520fa7f681SAndrew Gallatin } 11530fa7f681SAndrew Gallatin 11540fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 11552a0f8518SGleb Smirnoff ctx.sc = sc; 11562a0f8518SGleb Smirnoff ctx.error = 0; 11572a0f8518SGleb Smirnoff if_foreach_llmaddr(ifp, mxge_add_maddr, &ctx); 11582a0f8518SGleb Smirnoff if (ctx.error != 0) { 11592a0f8518SGleb Smirnoff device_printf(sc->dev, "Failed MXGEFW_JOIN_MULTICAST_GROUP, " 11602a0f8518SGleb Smirnoff "error status:" "%d\t", ctx.error); 11610fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 11620fa7f681SAndrew Gallatin return; 11630fa7f681SAndrew Gallatin } 11642a0f8518SGleb Smirnoff 11650fa7f681SAndrew Gallatin /* Enable multicast filtering */ 11660fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 11670fa7f681SAndrew Gallatin if (err != 0) { 11680fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 11690fa7f681SAndrew Gallatin ", error status: %d\n", err); 11700fa7f681SAndrew Gallatin } 11710fa7f681SAndrew Gallatin } 11720fa7f681SAndrew Gallatin 1173b2fc195eSAndrew Gallatin static int 1174053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc) 1175053e637fSAndrew Gallatin { 1176053e637fSAndrew Gallatin mxge_cmd_t cmd; 1177053e637fSAndrew Gallatin int status; 1178053e637fSAndrew Gallatin 1179c792928fSAndrew Gallatin if (MJUMPAGESIZE - MXGEFW_PAD > MXGEFW_MAX_MTU) 1180c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1181053e637fSAndrew Gallatin 1182053e637fSAndrew Gallatin /* try to set nbufs to see if it we can 1183053e637fSAndrew Gallatin use virtually contiguous jumbos */ 1184053e637fSAndrew Gallatin cmd.data0 = 0; 1185053e637fSAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 1186053e637fSAndrew Gallatin &cmd); 1187053e637fSAndrew Gallatin if (status == 0) 1188c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1189053e637fSAndrew Gallatin 1190053e637fSAndrew Gallatin /* otherwise, we're limited to MJUMPAGESIZE */ 1191053e637fSAndrew Gallatin return MJUMPAGESIZE - MXGEFW_PAD; 1192053e637fSAndrew Gallatin } 1193053e637fSAndrew Gallatin 1194053e637fSAndrew Gallatin static int 1195adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup) 1196b2fc195eSAndrew Gallatin { 11971e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 11981e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done; 11991e413cf9SAndrew Gallatin volatile uint32_t *irq_claim; 12006d87a65dSAndrew Gallatin mxge_cmd_t cmd; 12011e413cf9SAndrew Gallatin int slice, status; 1202b2fc195eSAndrew Gallatin 1203b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 1204b2fc195eSAndrew Gallatin is alive */ 1205b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 12065e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 1207b2fc195eSAndrew Gallatin if (status != 0) { 1208b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 1209b2fc195eSAndrew Gallatin return ENXIO; 1210b2fc195eSAndrew Gallatin } 1211b2fc195eSAndrew Gallatin 1212091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 1213091feecdSAndrew Gallatin 12141e413cf9SAndrew Gallatin 12151e413cf9SAndrew Gallatin /* set the intrq size */ 12161e413cf9SAndrew Gallatin cmd.data0 = sc->rx_ring_size; 12171e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 12181e413cf9SAndrew Gallatin 12191e413cf9SAndrew Gallatin /* 12201e413cf9SAndrew Gallatin * Even though we already know how many slices are supported 12211e413cf9SAndrew Gallatin * via mxge_slice_probe(), MXGEFW_CMD_GET_MAX_RSS_QUEUES 12221e413cf9SAndrew Gallatin * has magic side effects, and must be called after a reset. 12231e413cf9SAndrew Gallatin * It must be called prior to calling any RSS related cmds, 12241e413cf9SAndrew Gallatin * including assigning an interrupt queue for anything but 12251e413cf9SAndrew Gallatin * slice 0. It must also be called *after* 12261e413cf9SAndrew Gallatin * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by 12271e413cf9SAndrew Gallatin * the firmware to compute offsets. 12281e413cf9SAndrew Gallatin */ 12291e413cf9SAndrew Gallatin 12301e413cf9SAndrew Gallatin if (sc->num_slices > 1) { 12311e413cf9SAndrew Gallatin /* ask the maximum number of slices it supports */ 12321e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, 12331e413cf9SAndrew Gallatin &cmd); 12341e413cf9SAndrew Gallatin if (status != 0) { 12351e413cf9SAndrew Gallatin device_printf(sc->dev, 12361e413cf9SAndrew Gallatin "failed to get number of slices\n"); 12371e413cf9SAndrew Gallatin return status; 12381e413cf9SAndrew Gallatin } 12391e413cf9SAndrew Gallatin /* 12401e413cf9SAndrew Gallatin * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior 12411e413cf9SAndrew Gallatin * to setting up the interrupt queue DMA 12421e413cf9SAndrew Gallatin */ 12431e413cf9SAndrew Gallatin cmd.data0 = sc->num_slices; 12441e413cf9SAndrew Gallatin cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE; 1245c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 1246c6cb3e3fSAndrew Gallatin cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES; 1247c6cb3e3fSAndrew Gallatin #endif 12481e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ENABLE_RSS_QUEUES, 12491e413cf9SAndrew Gallatin &cmd); 12501e413cf9SAndrew Gallatin if (status != 0) { 12511e413cf9SAndrew Gallatin device_printf(sc->dev, 12521e413cf9SAndrew Gallatin "failed to set number of slices\n"); 12531e413cf9SAndrew Gallatin return status; 12541e413cf9SAndrew Gallatin } 12551e413cf9SAndrew Gallatin } 12561e413cf9SAndrew Gallatin 12571e413cf9SAndrew Gallatin 1258adae7080SAndrew Gallatin if (interrupts_setup) { 1259b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 12601e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 12611e413cf9SAndrew Gallatin rx_done = &sc->ss[slice].rx_done; 12621e413cf9SAndrew Gallatin memset(rx_done->entry, 0, sc->rx_ring_size); 12631e413cf9SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(rx_done->dma.bus_addr); 12641e413cf9SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(rx_done->dma.bus_addr); 12651e413cf9SAndrew Gallatin cmd.data2 = slice; 12661e413cf9SAndrew Gallatin status |= mxge_send_cmd(sc, 12671e413cf9SAndrew Gallatin MXGEFW_CMD_SET_INTRQ_DMA, 12681e413cf9SAndrew Gallatin &cmd); 12691e413cf9SAndrew Gallatin } 1270adae7080SAndrew Gallatin } 1271b2fc195eSAndrew Gallatin 12726d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 12735e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 12745e7d8541SAndrew Gallatin 12755e7d8541SAndrew Gallatin 12765e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 12775e7d8541SAndrew Gallatin 12785e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 12791e413cf9SAndrew Gallatin irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 12805e7d8541SAndrew Gallatin 12815e7d8541SAndrew Gallatin 12825e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 12836d87a65dSAndrew Gallatin &cmd); 12845e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 1285b2fc195eSAndrew Gallatin if (status != 0) { 1286b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 1287b2fc195eSAndrew Gallatin return status; 1288b2fc195eSAndrew Gallatin } 1289b2fc195eSAndrew Gallatin 12905e7d8541SAndrew Gallatin 12915e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 12925e7d8541SAndrew Gallatin 12935e7d8541SAndrew Gallatin 12945e7d8541SAndrew Gallatin /* run a DMA benchmark */ 12958fe615baSAndrew Gallatin (void) mxge_dma_test(sc, MXGEFW_DMA_TEST); 12965e7d8541SAndrew Gallatin 12971e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 12981e413cf9SAndrew Gallatin ss = &sc->ss[slice]; 12991e413cf9SAndrew Gallatin 13001e413cf9SAndrew Gallatin ss->irq_claim = irq_claim + (2 * slice); 1301b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 13021e413cf9SAndrew Gallatin ss->rx_done.idx = 0; 13031e413cf9SAndrew Gallatin ss->rx_done.cnt = 0; 13041e413cf9SAndrew Gallatin ss->tx.req = 0; 13051e413cf9SAndrew Gallatin ss->tx.done = 0; 13061e413cf9SAndrew Gallatin ss->tx.pkt_done = 0; 1307c6cb3e3fSAndrew Gallatin ss->tx.queue_active = 0; 1308c6cb3e3fSAndrew Gallatin ss->tx.activate = 0; 1309c6cb3e3fSAndrew Gallatin ss->tx.deactivate = 0; 13101e413cf9SAndrew Gallatin ss->tx.wake = 0; 13111e413cf9SAndrew Gallatin ss->tx.defrag = 0; 13121e413cf9SAndrew Gallatin ss->tx.stall = 0; 13131e413cf9SAndrew Gallatin ss->rx_big.cnt = 0; 13141e413cf9SAndrew Gallatin ss->rx_small.cnt = 0; 131526dd49c6SAndrew Gallatin ss->lc.lro_bad_csum = 0; 131626dd49c6SAndrew Gallatin ss->lc.lro_queued = 0; 131726dd49c6SAndrew Gallatin ss->lc.lro_flushed = 0; 13181e413cf9SAndrew Gallatin if (ss->fw_stats != NULL) { 1319a393336bSAndrew Gallatin bzero(ss->fw_stats, sizeof *ss->fw_stats); 13201e413cf9SAndrew Gallatin } 13211e413cf9SAndrew Gallatin } 1322b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 13236d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 1324bb8ddc66SAndrew Gallatin mxge_change_promisc(sc, sc->ifp->if_flags & IFF_PROMISC); 13256d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 13260fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 132765c69066SAndrew Gallatin if (sc->throttle) { 132865c69066SAndrew Gallatin cmd.data0 = sc->throttle; 132965c69066SAndrew Gallatin if (mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, 133065c69066SAndrew Gallatin &cmd)) { 133165c69066SAndrew Gallatin device_printf(sc->dev, 133265c69066SAndrew Gallatin "can't enable throttle\n"); 133365c69066SAndrew Gallatin } 133465c69066SAndrew Gallatin } 1335b2fc195eSAndrew Gallatin return status; 1336b2fc195eSAndrew Gallatin } 1337b2fc195eSAndrew Gallatin 1338b2fc195eSAndrew Gallatin static int 133965c69066SAndrew Gallatin mxge_change_throttle(SYSCTL_HANDLER_ARGS) 134065c69066SAndrew Gallatin { 134165c69066SAndrew Gallatin mxge_cmd_t cmd; 134265c69066SAndrew Gallatin mxge_softc_t *sc; 134365c69066SAndrew Gallatin int err; 134465c69066SAndrew Gallatin unsigned int throttle; 134565c69066SAndrew Gallatin 134665c69066SAndrew Gallatin sc = arg1; 134765c69066SAndrew Gallatin throttle = sc->throttle; 134865c69066SAndrew Gallatin err = sysctl_handle_int(oidp, &throttle, arg2, req); 134965c69066SAndrew Gallatin if (err != 0) { 135065c69066SAndrew Gallatin return err; 135165c69066SAndrew Gallatin } 135265c69066SAndrew Gallatin 135365c69066SAndrew Gallatin if (throttle == sc->throttle) 135465c69066SAndrew Gallatin return 0; 135565c69066SAndrew Gallatin 135665c69066SAndrew Gallatin if (throttle < MXGE_MIN_THROTTLE || throttle > MXGE_MAX_THROTTLE) 135765c69066SAndrew Gallatin return EINVAL; 135865c69066SAndrew Gallatin 135965c69066SAndrew Gallatin mtx_lock(&sc->driver_mtx); 136065c69066SAndrew Gallatin cmd.data0 = throttle; 136165c69066SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_THROTTLE_FACTOR, &cmd); 136265c69066SAndrew Gallatin if (err == 0) 136365c69066SAndrew Gallatin sc->throttle = throttle; 136465c69066SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 136565c69066SAndrew Gallatin return err; 136665c69066SAndrew Gallatin } 136765c69066SAndrew Gallatin 136865c69066SAndrew Gallatin static int 13696d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1370b2fc195eSAndrew Gallatin { 13716d87a65dSAndrew Gallatin mxge_softc_t *sc; 1372b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1373b2fc195eSAndrew Gallatin int err; 1374b2fc195eSAndrew Gallatin 1375b2fc195eSAndrew Gallatin sc = arg1; 1376b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1377b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1378b2fc195eSAndrew Gallatin if (err != 0) { 1379b2fc195eSAndrew Gallatin return err; 1380b2fc195eSAndrew Gallatin } 1381b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1382b2fc195eSAndrew Gallatin return 0; 1383b2fc195eSAndrew Gallatin 1384b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1385b2fc195eSAndrew Gallatin return EINVAL; 1386b2fc195eSAndrew Gallatin 1387a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 13885e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1389b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 13905e7d8541SAndrew Gallatin 1391a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1392b2fc195eSAndrew Gallatin return err; 1393b2fc195eSAndrew Gallatin } 1394b2fc195eSAndrew Gallatin 1395b2fc195eSAndrew Gallatin static int 13966d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1397b2fc195eSAndrew Gallatin { 13986d87a65dSAndrew Gallatin mxge_softc_t *sc; 1399b2fc195eSAndrew Gallatin unsigned int enabled; 1400b2fc195eSAndrew Gallatin int err; 1401b2fc195eSAndrew Gallatin 1402b2fc195eSAndrew Gallatin sc = arg1; 1403b2fc195eSAndrew Gallatin enabled = sc->pause; 1404b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1405b2fc195eSAndrew Gallatin if (err != 0) { 1406b2fc195eSAndrew Gallatin return err; 1407b2fc195eSAndrew Gallatin } 1408b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1409b2fc195eSAndrew Gallatin return 0; 1410b2fc195eSAndrew Gallatin 1411a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 14126d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1413a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1414b2fc195eSAndrew Gallatin return err; 1415b2fc195eSAndrew Gallatin } 1416b2fc195eSAndrew Gallatin 1417b2fc195eSAndrew Gallatin static int 14186d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1419b2fc195eSAndrew Gallatin { 1420b2fc195eSAndrew Gallatin int err; 1421b2fc195eSAndrew Gallatin 1422b2fc195eSAndrew Gallatin if (arg1 == NULL) 1423b2fc195eSAndrew Gallatin return EFAULT; 1424b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1425b2fc195eSAndrew Gallatin arg1 = NULL; 1426b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1427b2fc195eSAndrew Gallatin 1428b2fc195eSAndrew Gallatin return err; 1429b2fc195eSAndrew Gallatin } 1430b2fc195eSAndrew Gallatin 1431b2fc195eSAndrew Gallatin static void 14321e413cf9SAndrew Gallatin mxge_rem_sysctls(mxge_softc_t *sc) 14331e413cf9SAndrew Gallatin { 14341e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 14351e413cf9SAndrew Gallatin int slice; 14361e413cf9SAndrew Gallatin 14371e413cf9SAndrew Gallatin if (sc->slice_sysctl_tree == NULL) 14381e413cf9SAndrew Gallatin return; 14391e413cf9SAndrew Gallatin 14401e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 14411e413cf9SAndrew Gallatin ss = &sc->ss[slice]; 14421e413cf9SAndrew Gallatin if (ss == NULL || ss->sysctl_tree == NULL) 14431e413cf9SAndrew Gallatin continue; 14441e413cf9SAndrew Gallatin sysctl_ctx_free(&ss->sysctl_ctx); 14451e413cf9SAndrew Gallatin ss->sysctl_tree = NULL; 14461e413cf9SAndrew Gallatin } 14471e413cf9SAndrew Gallatin sysctl_ctx_free(&sc->slice_sysctl_ctx); 14481e413cf9SAndrew Gallatin sc->slice_sysctl_tree = NULL; 14491e413cf9SAndrew Gallatin } 14501e413cf9SAndrew Gallatin 14511e413cf9SAndrew Gallatin static void 14526d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1453b2fc195eSAndrew Gallatin { 1454b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1455b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 14565e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 14571e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 14581e413cf9SAndrew Gallatin int slice; 14591e413cf9SAndrew Gallatin char slice_num[8]; 1460b2fc195eSAndrew Gallatin 1461b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1462b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 14631e413cf9SAndrew Gallatin fw = sc->ss[0].fw_stats; 1464b2fc195eSAndrew Gallatin 14655e7d8541SAndrew Gallatin /* random information */ 14665e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 14675e7d8541SAndrew Gallatin "firmware_version", 1468f0188618SHans Petter Selasky CTLFLAG_RD, sc->fw_version, 14695e7d8541SAndrew Gallatin 0, "firmware version"); 14705e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 14715e7d8541SAndrew Gallatin "serial_number", 1472f0188618SHans Petter Selasky CTLFLAG_RD, sc->serial_number_string, 14735e7d8541SAndrew Gallatin 0, "serial number"); 14745e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 14755e7d8541SAndrew Gallatin "product_code", 1476f0188618SHans Petter Selasky CTLFLAG_RD, sc->product_code_string, 14775e7d8541SAndrew Gallatin 0, "product_code"); 14785e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1479d91b1b49SAndrew Gallatin "pcie_link_width", 1480d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1481d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1482d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14835e7d8541SAndrew Gallatin "tx_boundary", 14841e413cf9SAndrew Gallatin CTLFLAG_RD, &sc->tx_boundary, 14855e7d8541SAndrew Gallatin 0, "tx_boundary"); 14865e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1487091feecdSAndrew Gallatin "write_combine", 1488091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1489091feecdSAndrew Gallatin 0, "write combining PIO?"); 1490091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14915e7d8541SAndrew Gallatin "read_dma_MBs", 14925e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 14935e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 14945e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14955e7d8541SAndrew Gallatin "write_dma_MBs", 14965e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 14975e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 14985e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14995e7d8541SAndrew Gallatin "read_write_dma_MBs", 15005e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 15015e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 1502a393336bSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1503a393336bSAndrew Gallatin "watchdog_resets", 1504a393336bSAndrew Gallatin CTLFLAG_RD, &sc->watchdog_resets, 1505a393336bSAndrew Gallatin 0, "Number of times NIC was reset"); 15065e7d8541SAndrew Gallatin 15075e7d8541SAndrew Gallatin 15085e7d8541SAndrew Gallatin /* performance related tunables */ 1509b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15107029da5cSPawel Biernacki "intr_coal_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 15117029da5cSPawel Biernacki sc, 0, mxge_change_intr_coal, "I", 15127029da5cSPawel Biernacki "interrupt coalescing delay in usecs"); 1513b2fc195eSAndrew Gallatin 1514b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15157029da5cSPawel Biernacki "throttle", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, 15167029da5cSPawel Biernacki mxge_change_throttle, "I", "transmit throttling"); 151765c69066SAndrew Gallatin 151865c69066SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1519b2fc195eSAndrew Gallatin "flow_control_enabled", 15207029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, 15217029da5cSPawel Biernacki mxge_change_flow_control, "I", 15227029da5cSPawel Biernacki "interrupt coalescing delay in usecs"); 1523b2fc195eSAndrew Gallatin 1524b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 15255e7d8541SAndrew Gallatin "deassert_wait", 15265e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 15275e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1528b2fc195eSAndrew Gallatin 1529b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1530b2fc195eSAndrew Gallatin Need to swap it */ 1531b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15327029da5cSPawel Biernacki "link_up", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15337029da5cSPawel Biernacki &fw->link_up, 0, mxge_handle_be32, "I", "link up"); 1534b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15357029da5cSPawel Biernacki "rdma_tags_available", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15367029da5cSPawel Biernacki &fw->rdma_tags_available, 0, mxge_handle_be32, "I", 15377029da5cSPawel Biernacki "rdma_tags_available"); 1538b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15397029da5cSPawel Biernacki "dropped_bad_crc32", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15407029da5cSPawel Biernacki &fw->dropped_bad_crc32, 0, mxge_handle_be32, "I", 15417029da5cSPawel Biernacki "dropped_bad_crc32"); 1542adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15437029da5cSPawel Biernacki "dropped_bad_phy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15447029da5cSPawel Biernacki &fw->dropped_bad_phy, 0, mxge_handle_be32, "I", "dropped_bad_phy"); 1545b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1546b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 15477029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15487029da5cSPawel Biernacki &fw->dropped_link_error_or_filtered, 0, mxge_handle_be32, "I", 15497029da5cSPawel Biernacki "dropped_link_error_or_filtered"); 1550b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1551adae7080SAndrew Gallatin "dropped_link_overflow", 15527029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15537029da5cSPawel Biernacki &fw->dropped_link_overflow, 0, mxge_handle_be32, "I", 15547029da5cSPawel Biernacki "dropped_link_overflow"); 1555adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15560fa7f681SAndrew Gallatin "dropped_multicast_filtered", 15577029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15587029da5cSPawel Biernacki &fw->dropped_multicast_filtered, 0, mxge_handle_be32, "I", 15597029da5cSPawel Biernacki "dropped_multicast_filtered"); 15600fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1561adae7080SAndrew Gallatin "dropped_no_big_buffer", 15627029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15637029da5cSPawel Biernacki &fw->dropped_no_big_buffer, 0, mxge_handle_be32, "I", 15647029da5cSPawel Biernacki "dropped_no_big_buffer"); 1565b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1566b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 15677029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15687029da5cSPawel Biernacki &fw->dropped_no_small_buffer, 0, mxge_handle_be32, "I", 15697029da5cSPawel Biernacki "dropped_no_small_buffer"); 1570b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1571adae7080SAndrew Gallatin "dropped_overrun", 15727029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15737029da5cSPawel Biernacki &fw->dropped_overrun, 0, mxge_handle_be32, "I", 15747029da5cSPawel Biernacki "dropped_overrun"); 1575adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15767029da5cSPawel Biernacki "dropped_pause", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15777029da5cSPawel Biernacki &fw->dropped_pause, 0, mxge_handle_be32, "I", "dropped_pause"); 1578adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 15797029da5cSPawel Biernacki "dropped_runt", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15807029da5cSPawel Biernacki &fw->dropped_runt, 0, mxge_handle_be32, "I", "dropped_runt"); 1581b2fc195eSAndrew Gallatin 1582a0394e33SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1583a0394e33SAndrew Gallatin "dropped_unicast_filtered", 15847029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 15857029da5cSPawel Biernacki &fw->dropped_unicast_filtered, 0, mxge_handle_be32, "I", 15867029da5cSPawel Biernacki "dropped_unicast_filtered"); 1587a0394e33SAndrew Gallatin 15885e7d8541SAndrew Gallatin /* verbose printing? */ 1589b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 15905e7d8541SAndrew Gallatin "verbose", 15915e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 15925e7d8541SAndrew Gallatin 0, "verbose printing"); 1593b2fc195eSAndrew Gallatin 15941e413cf9SAndrew Gallatin /* add counters exported for debugging from all slices */ 15951e413cf9SAndrew Gallatin sysctl_ctx_init(&sc->slice_sysctl_ctx); 15961e413cf9SAndrew Gallatin sc->slice_sysctl_tree = 15971e413cf9SAndrew Gallatin SYSCTL_ADD_NODE(&sc->slice_sysctl_ctx, children, OID_AUTO, 15987029da5cSPawel Biernacki "slice", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 15991e413cf9SAndrew Gallatin 16001e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 16011e413cf9SAndrew Gallatin ss = &sc->ss[slice]; 16021e413cf9SAndrew Gallatin sysctl_ctx_init(&ss->sysctl_ctx); 16031e413cf9SAndrew Gallatin ctx = &ss->sysctl_ctx; 16041e413cf9SAndrew Gallatin children = SYSCTL_CHILDREN(sc->slice_sysctl_tree); 16051e413cf9SAndrew Gallatin sprintf(slice_num, "%d", slice); 16061e413cf9SAndrew Gallatin ss->sysctl_tree = 16071e413cf9SAndrew Gallatin SYSCTL_ADD_NODE(ctx, children, OID_AUTO, slice_num, 16087029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 16091e413cf9SAndrew Gallatin children = SYSCTL_CHILDREN(ss->sysctl_tree); 1610053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16111e413cf9SAndrew Gallatin "rx_small_cnt", 16121e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->rx_small.cnt, 16131e413cf9SAndrew Gallatin 0, "rx_small_cnt"); 16141e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16151e413cf9SAndrew Gallatin "rx_big_cnt", 16161e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->rx_big.cnt, 16171e413cf9SAndrew Gallatin 0, "rx_small_cnt"); 1618e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO, 161926dd49c6SAndrew Gallatin "lro_flushed", CTLFLAG_RD, &ss->lc.lro_flushed, 1620053e637fSAndrew Gallatin 0, "number of lro merge queues flushed"); 1621053e637fSAndrew Gallatin 1622e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO, 162326dd49c6SAndrew Gallatin "lro_bad_csum", CTLFLAG_RD, &ss->lc.lro_bad_csum, 162426dd49c6SAndrew Gallatin 0, "number of bad csums preventing LRO"); 162526dd49c6SAndrew Gallatin 1626e936121dSHans Petter Selasky SYSCTL_ADD_U64(ctx, children, OID_AUTO, 162726dd49c6SAndrew Gallatin "lro_queued", CTLFLAG_RD, &ss->lc.lro_queued, 16281e413cf9SAndrew Gallatin 0, "number of frames appended to lro merge" 16291e413cf9SAndrew Gallatin "queues"); 1630053e637fSAndrew Gallatin 1631c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 16321e413cf9SAndrew Gallatin /* only transmit from slice 0 for now */ 16331e413cf9SAndrew Gallatin if (slice > 0) 16341e413cf9SAndrew Gallatin continue; 1635c6cb3e3fSAndrew Gallatin #endif 1636c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1637c6cb3e3fSAndrew Gallatin "tx_req", 1638c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.req, 1639c6cb3e3fSAndrew Gallatin 0, "tx_req"); 16401e413cf9SAndrew Gallatin 16411e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16421e413cf9SAndrew Gallatin "tx_done", 16431e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.done, 16441e413cf9SAndrew Gallatin 0, "tx_done"); 16451e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16461e413cf9SAndrew Gallatin "tx_pkt_done", 16471e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.pkt_done, 16481e413cf9SAndrew Gallatin 0, "tx_done"); 16491e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16501e413cf9SAndrew Gallatin "tx_stall", 16511e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.stall, 16521e413cf9SAndrew Gallatin 0, "tx_stall"); 16531e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16541e413cf9SAndrew Gallatin "tx_wake", 16551e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.wake, 16561e413cf9SAndrew Gallatin 0, "tx_wake"); 16571e413cf9SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 16581e413cf9SAndrew Gallatin "tx_defrag", 16591e413cf9SAndrew Gallatin CTLFLAG_RD, &ss->tx.defrag, 16601e413cf9SAndrew Gallatin 0, "tx_defrag"); 1661c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1662c6cb3e3fSAndrew Gallatin "tx_queue_active", 1663c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.queue_active, 1664c6cb3e3fSAndrew Gallatin 0, "tx_queue_active"); 1665c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1666c6cb3e3fSAndrew Gallatin "tx_activate", 1667c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.activate, 1668c6cb3e3fSAndrew Gallatin 0, "tx_activate"); 1669c6cb3e3fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1670c6cb3e3fSAndrew Gallatin "tx_deactivate", 1671c6cb3e3fSAndrew Gallatin CTLFLAG_RD, &ss->tx.deactivate, 1672c6cb3e3fSAndrew Gallatin 0, "tx_deactivate"); 16731e413cf9SAndrew Gallatin } 1674b2fc195eSAndrew Gallatin } 1675b2fc195eSAndrew Gallatin 1676b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1677b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1678b2fc195eSAndrew Gallatin 1679b2fc195eSAndrew Gallatin static inline void 16801e413cf9SAndrew Gallatin mxge_submit_req_backwards(mxge_tx_ring_t *tx, 1681b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1682b2fc195eSAndrew Gallatin { 1683b2fc195eSAndrew Gallatin int idx, starting_slot; 1684b2fc195eSAndrew Gallatin starting_slot = tx->req; 1685b2fc195eSAndrew Gallatin while (cnt > 1) { 1686b2fc195eSAndrew Gallatin cnt--; 1687b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 16886d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1689b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 169073c7c83fSAndrew Gallatin wmb(); 1691b2fc195eSAndrew Gallatin } 1692b2fc195eSAndrew Gallatin } 1693b2fc195eSAndrew Gallatin 1694b2fc195eSAndrew Gallatin /* 1695b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1696b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1697b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1698b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1699b2fc195eSAndrew Gallatin */ 1700b2fc195eSAndrew Gallatin 1701b2fc195eSAndrew Gallatin static inline void 17021e413cf9SAndrew Gallatin mxge_submit_req(mxge_tx_ring_t *tx, mcp_kreq_ether_send_t *src, 1703b2fc195eSAndrew Gallatin int cnt) 1704b2fc195eSAndrew Gallatin { 1705b2fc195eSAndrew Gallatin int idx, i; 1706b2fc195eSAndrew Gallatin uint32_t *src_ints; 1707b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1708b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1709b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 17105e7d8541SAndrew Gallatin uint8_t last_flags; 1711b2fc195eSAndrew Gallatin 1712b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1713b2fc195eSAndrew Gallatin 17145e7d8541SAndrew Gallatin last_flags = src->flags; 17155e7d8541SAndrew Gallatin src->flags = 0; 171673c7c83fSAndrew Gallatin wmb(); 1717b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1718b2fc195eSAndrew Gallatin srcp = src; 1719b2fc195eSAndrew Gallatin 1720b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1721b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 17226d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 172373c7c83fSAndrew Gallatin wmb(); /* force write every 32 bytes */ 1724b2fc195eSAndrew Gallatin srcp += 2; 1725b2fc195eSAndrew Gallatin dstp += 2; 1726b2fc195eSAndrew Gallatin } 1727b2fc195eSAndrew Gallatin } else { 1728b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1729b2fc195eSAndrew Gallatin that it is submitted below */ 17306d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1731b2fc195eSAndrew Gallatin i = 0; 1732b2fc195eSAndrew Gallatin } 1733b2fc195eSAndrew Gallatin if (i < cnt) { 1734b2fc195eSAndrew Gallatin /* submit the first request */ 17356d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 173673c7c83fSAndrew Gallatin wmb(); /* barrier before setting valid flag */ 1737b2fc195eSAndrew Gallatin } 1738b2fc195eSAndrew Gallatin 1739b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 17405e7d8541SAndrew Gallatin src->flags = last_flags; 1741b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1742b2fc195eSAndrew Gallatin src_ints+=3; 1743b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1744b2fc195eSAndrew Gallatin dst_ints+=3; 1745b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1746b2fc195eSAndrew Gallatin tx->req += cnt; 174773c7c83fSAndrew Gallatin wmb(); 1748b2fc195eSAndrew Gallatin } 1749b2fc195eSAndrew Gallatin 17500a7a780eSAndrew Gallatin static int 17510a7a780eSAndrew Gallatin mxge_parse_tx(struct mxge_slice_state *ss, struct mbuf *m, 17520a7a780eSAndrew Gallatin struct mxge_pkt_info *pi) 17530a7a780eSAndrew Gallatin { 17540a7a780eSAndrew Gallatin struct ether_vlan_header *eh; 17550a7a780eSAndrew Gallatin uint16_t etype; 17560a7a780eSAndrew Gallatin int tso = m->m_pkthdr.csum_flags & (CSUM_TSO); 17570a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6) 17580a7a780eSAndrew Gallatin int nxt; 17590a7a780eSAndrew Gallatin #endif 17600a7a780eSAndrew Gallatin 17610a7a780eSAndrew Gallatin eh = mtod(m, struct ether_vlan_header *); 17620a7a780eSAndrew Gallatin if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 17630a7a780eSAndrew Gallatin etype = ntohs(eh->evl_proto); 17640a7a780eSAndrew Gallatin pi->ip_off = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 17650a7a780eSAndrew Gallatin } else { 17660a7a780eSAndrew Gallatin etype = ntohs(eh->evl_encap_proto); 17670a7a780eSAndrew Gallatin pi->ip_off = ETHER_HDR_LEN; 17680a7a780eSAndrew Gallatin } 17690a7a780eSAndrew Gallatin 17700a7a780eSAndrew Gallatin switch (etype) { 17710a7a780eSAndrew Gallatin case ETHERTYPE_IP: 17720a7a780eSAndrew Gallatin /* 17730a7a780eSAndrew Gallatin * ensure ip header is in first mbuf, copy it to a 17740a7a780eSAndrew Gallatin * scratch buffer if not 17750a7a780eSAndrew Gallatin */ 17760a7a780eSAndrew Gallatin pi->ip = (struct ip *)(m->m_data + pi->ip_off); 17770a7a780eSAndrew Gallatin pi->ip6 = NULL; 17780a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip))) { 17790a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip), 17800a7a780eSAndrew Gallatin ss->scratch); 17810a7a780eSAndrew Gallatin pi->ip = (struct ip *)(ss->scratch + pi->ip_off); 17820a7a780eSAndrew Gallatin } 17830a7a780eSAndrew Gallatin pi->ip_hlen = pi->ip->ip_hl << 2; 17840a7a780eSAndrew Gallatin if (!tso) 17850a7a780eSAndrew Gallatin return 0; 17860a7a780eSAndrew Gallatin 17870a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen + 17880a7a780eSAndrew Gallatin sizeof(struct tcphdr))) { 17890a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + pi->ip_hlen + 17900a7a780eSAndrew Gallatin sizeof(struct tcphdr), ss->scratch); 17910a7a780eSAndrew Gallatin pi->ip = (struct ip *)(ss->scratch + pi->ip_off); 17920a7a780eSAndrew Gallatin } 17930a7a780eSAndrew Gallatin pi->tcp = (struct tcphdr *)((char *)pi->ip + pi->ip_hlen); 17940a7a780eSAndrew Gallatin break; 17950a7a780eSAndrew Gallatin #if IFCAP_TSO6 && defined(INET6) 17960a7a780eSAndrew Gallatin case ETHERTYPE_IPV6: 17970a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(m->m_data + pi->ip_off); 17980a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + sizeof(*pi->ip6))) { 17990a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + sizeof(*pi->ip6), 18000a7a780eSAndrew Gallatin ss->scratch); 18010a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off); 18020a7a780eSAndrew Gallatin } 18030a7a780eSAndrew Gallatin nxt = 0; 18040a7a780eSAndrew Gallatin pi->ip_hlen = ip6_lasthdr(m, pi->ip_off, IPPROTO_IPV6, &nxt); 18050a7a780eSAndrew Gallatin pi->ip_hlen -= pi->ip_off; 18060a7a780eSAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) 18070a7a780eSAndrew Gallatin return EINVAL; 18080a7a780eSAndrew Gallatin 18090a7a780eSAndrew Gallatin if (!tso) 18100a7a780eSAndrew Gallatin return 0; 18110a7a780eSAndrew Gallatin 18120a7a780eSAndrew Gallatin if (pi->ip_off + pi->ip_hlen > ss->sc->max_tso6_hlen) 18130a7a780eSAndrew Gallatin return EINVAL; 18140a7a780eSAndrew Gallatin 18150a7a780eSAndrew Gallatin if (__predict_false(m->m_len < pi->ip_off + pi->ip_hlen + 18160a7a780eSAndrew Gallatin sizeof(struct tcphdr))) { 18170a7a780eSAndrew Gallatin m_copydata(m, 0, pi->ip_off + pi->ip_hlen + 18180a7a780eSAndrew Gallatin sizeof(struct tcphdr), ss->scratch); 18190a7a780eSAndrew Gallatin pi->ip6 = (struct ip6_hdr *)(ss->scratch + pi->ip_off); 18200a7a780eSAndrew Gallatin } 18210a7a780eSAndrew Gallatin pi->tcp = (struct tcphdr *)((char *)pi->ip6 + pi->ip_hlen); 18220a7a780eSAndrew Gallatin break; 18230a7a780eSAndrew Gallatin #endif 18240a7a780eSAndrew Gallatin default: 18250a7a780eSAndrew Gallatin return EINVAL; 18260a7a780eSAndrew Gallatin } 18270a7a780eSAndrew Gallatin return 0; 18280a7a780eSAndrew Gallatin } 18290a7a780eSAndrew Gallatin 183037d89b0cSAndrew Gallatin #if IFCAP_TSO4 183137d89b0cSAndrew Gallatin 1832b2fc195eSAndrew Gallatin static void 18331e413cf9SAndrew Gallatin mxge_encap_tso(struct mxge_slice_state *ss, struct mbuf *m, 18340a7a780eSAndrew Gallatin int busdma_seg_cnt, struct mxge_pkt_info *pi) 1835aed8e389SAndrew Gallatin { 18361e413cf9SAndrew Gallatin mxge_tx_ring_t *tx; 1837aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1838aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1839aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1840aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1841aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 18420a7a780eSAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss, sum; 1843aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1844aed8e389SAndrew Gallatin static int once; 1845aed8e389SAndrew Gallatin 1846aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1847aed8e389SAndrew Gallatin 1848aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1849aed8e389SAndrew Gallatin * send loop that we are still in the 1850aed8e389SAndrew Gallatin * header portion of the TSO packet. 1851aed8e389SAndrew Gallatin */ 1852aed8e389SAndrew Gallatin 18530a7a780eSAndrew Gallatin cksum_offset = pi->ip_off + pi->ip_hlen; 18540a7a780eSAndrew Gallatin cum_len = -(cksum_offset + (pi->tcp->th_off << 2)); 1855aed8e389SAndrew Gallatin 1856aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 18570a7a780eSAndrew Gallatin if (__predict_false((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_TCP_IPV6)) == 0)) { 18584ed8ca8fSAndrew Gallatin /* 18594ed8ca8fSAndrew Gallatin * If packet has full TCP csum, replace it with pseudo hdr 18604ed8ca8fSAndrew Gallatin * sum that the NIC expects, otherwise the NIC will emit 18614ed8ca8fSAndrew Gallatin * packets with bad TCP checksums. 18624ed8ca8fSAndrew Gallatin */ 18634ed8ca8fSAndrew Gallatin m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); 18640a7a780eSAndrew Gallatin if (pi->ip6) { 18650a7a780eSAndrew Gallatin #if (CSUM_TCP_IPV6 != 0) && defined(INET6) 18660a7a780eSAndrew Gallatin m->m_pkthdr.csum_flags |= CSUM_TCP_IPV6; 18670a7a780eSAndrew Gallatin sum = in6_cksum_pseudo(pi->ip6, 18680a7a780eSAndrew Gallatin m->m_pkthdr.len - cksum_offset, 18690a7a780eSAndrew Gallatin IPPROTO_TCP, 0); 18700a7a780eSAndrew Gallatin #endif 18710a7a780eSAndrew Gallatin } else { 1872abc5b96bSAndrew Gallatin #ifdef INET 18730a7a780eSAndrew Gallatin m->m_pkthdr.csum_flags |= CSUM_TCP; 18740a7a780eSAndrew Gallatin sum = in_pseudo(pi->ip->ip_src.s_addr, 18750a7a780eSAndrew Gallatin pi->ip->ip_dst.s_addr, 18760a7a780eSAndrew Gallatin htons(IPPROTO_TCP + (m->m_pkthdr.len - 18770a7a780eSAndrew Gallatin cksum_offset))); 1878abc5b96bSAndrew Gallatin #endif 18790a7a780eSAndrew Gallatin } 18800a7a780eSAndrew Gallatin m_copyback(m, offsetof(struct tcphdr, th_sum) + 18810a7a780eSAndrew Gallatin cksum_offset, sizeof(sum), (caddr_t)&sum); 18824ed8ca8fSAndrew Gallatin } 1883aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1884aed8e389SAndrew Gallatin 1885aed8e389SAndrew Gallatin 1886aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1887aed8e389SAndrew Gallatin * The firmware figures out where to put 1888aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1889aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1890aed8e389SAndrew Gallatin 18910a7a780eSAndrew Gallatin if (pi->ip6) { 18920a7a780eSAndrew Gallatin /* 18930a7a780eSAndrew Gallatin * for IPv6 TSO, the "checksum offset" is re-purposed 18940a7a780eSAndrew Gallatin * to store the TCP header len 18950a7a780eSAndrew Gallatin */ 18960a7a780eSAndrew Gallatin cksum_offset = (pi->tcp->th_off << 2); 18970a7a780eSAndrew Gallatin } 18980a7a780eSAndrew Gallatin 18991e413cf9SAndrew Gallatin tx = &ss->tx; 1900aed8e389SAndrew Gallatin req = tx->req_list; 1901aed8e389SAndrew Gallatin seg = tx->seg_list; 1902aed8e389SAndrew Gallatin cnt = 0; 1903aed8e389SAndrew Gallatin rdma_count = 0; 1904aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1905aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1906aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1907aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1908aed8e389SAndrew Gallatin * to 0 after a segment cut. 1909aed8e389SAndrew Gallatin * 1910aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1911aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1912aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1913aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1914aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1915aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1916aed8e389SAndrew Gallatin * 1917aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1918aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1919aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1920aed8e389SAndrew Gallatin */ 1921aed8e389SAndrew Gallatin 1922aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1923aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1924aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1925aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1926e39a0a37SAndrew Gallatin len = seg->ds_len; 1927aed8e389SAndrew Gallatin 1928aed8e389SAndrew Gallatin while (len) { 1929aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1930e39a0a37SAndrew Gallatin seglen = len; 1931aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1932aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1933aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1934aed8e389SAndrew Gallatin /* payload */ 1935aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1936aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1937aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1938aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1939aed8e389SAndrew Gallatin flags_next |= next_is_first * 1940aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1941aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1942aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1943aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1944aed8e389SAndrew Gallatin /* header ends */ 1945aed8e389SAndrew Gallatin rdma_count = -1; 1946aed8e389SAndrew Gallatin cum_len_next = 0; 1947aed8e389SAndrew Gallatin seglen = -cum_len; 1948aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1949aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1950aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1951aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1952aed8e389SAndrew Gallatin } 1953aed8e389SAndrew Gallatin 1954aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1955aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1956aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1957aed8e389SAndrew Gallatin req->pad = 0; 1958aed8e389SAndrew Gallatin req->rdma_count = 1; 1959aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1960aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1961aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1962aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1963aed8e389SAndrew Gallatin low += seglen; 1964aed8e389SAndrew Gallatin len -= seglen; 1965aed8e389SAndrew Gallatin cum_len = cum_len_next; 1966aed8e389SAndrew Gallatin flags = flags_next; 1967aed8e389SAndrew Gallatin req++; 1968aed8e389SAndrew Gallatin cnt++; 1969aed8e389SAndrew Gallatin rdma_count++; 19700a7a780eSAndrew Gallatin if (cksum_offset != 0 && !pi->ip6) { 1971aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1972aed8e389SAndrew Gallatin cksum_offset -= seglen; 1973aed8e389SAndrew Gallatin else 1974aed8e389SAndrew Gallatin cksum_offset = 0; 19750a7a780eSAndrew Gallatin } 1976adae7080SAndrew Gallatin if (__predict_false(cnt > tx->max_desc)) 1977aed8e389SAndrew Gallatin goto drop; 1978aed8e389SAndrew Gallatin } 1979aed8e389SAndrew Gallatin busdma_seg_cnt--; 1980aed8e389SAndrew Gallatin seg++; 1981aed8e389SAndrew Gallatin } 1982aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1983aed8e389SAndrew Gallatin 1984aed8e389SAndrew Gallatin do { 1985aed8e389SAndrew Gallatin req--; 1986aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1987aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1988aed8e389SAndrew Gallatin 1989aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1990aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1991c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 1992c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && tx->queue_active == 0) { 1993c6cb3e3fSAndrew Gallatin /* tell the NIC to start polling this slice */ 1994c6cb3e3fSAndrew Gallatin *tx->send_go = 1; 1995c6cb3e3fSAndrew Gallatin tx->queue_active = 1; 1996c6cb3e3fSAndrew Gallatin tx->activate++; 1997c6cb3e3fSAndrew Gallatin wmb(); 1998c6cb3e3fSAndrew Gallatin } 1999c6cb3e3fSAndrew Gallatin #endif 2000aed8e389SAndrew Gallatin return; 2001aed8e389SAndrew Gallatin 2002aed8e389SAndrew Gallatin drop: 2003e39a0a37SAndrew Gallatin bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map); 2004aed8e389SAndrew Gallatin m_freem(m); 2005c6cb3e3fSAndrew Gallatin ss->oerrors++; 2006aed8e389SAndrew Gallatin if (!once) { 2007adae7080SAndrew Gallatin printf("tx->max_desc exceeded via TSO!\n"); 2008adae7080SAndrew Gallatin printf("mss = %d, %ld, %d!\n", mss, 2009adae7080SAndrew Gallatin (long)seg - (long)tx->seg_list, tx->max_desc); 2010aed8e389SAndrew Gallatin once = 1; 2011aed8e389SAndrew Gallatin } 2012aed8e389SAndrew Gallatin return; 2013aed8e389SAndrew Gallatin 2014aed8e389SAndrew Gallatin } 2015aed8e389SAndrew Gallatin 201637d89b0cSAndrew Gallatin #endif /* IFCAP_TSO4 */ 201737d89b0cSAndrew Gallatin 201837d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API 2019c792928fSAndrew Gallatin /* 2020c792928fSAndrew Gallatin * We reproduce the software vlan tag insertion from 2021c792928fSAndrew Gallatin * net/if_vlan.c:vlan_start() here so that we can advertise "hardware" 2022c792928fSAndrew Gallatin * vlan tag insertion. We need to advertise this in order to have the 2023c792928fSAndrew Gallatin * vlan interface respect our csum offload flags. 2024c792928fSAndrew Gallatin */ 2025c792928fSAndrew Gallatin static struct mbuf * 2026c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m) 2027c792928fSAndrew Gallatin { 2028c792928fSAndrew Gallatin struct ether_vlan_header *evl; 2029c792928fSAndrew Gallatin 2030c6499eccSGleb Smirnoff M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT); 2031c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 2032c792928fSAndrew Gallatin return NULL; 2033c792928fSAndrew Gallatin if (m->m_len < sizeof(*evl)) { 2034c792928fSAndrew Gallatin m = m_pullup(m, sizeof(*evl)); 2035c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 2036c792928fSAndrew Gallatin return NULL; 2037c792928fSAndrew Gallatin } 2038c792928fSAndrew Gallatin /* 2039c792928fSAndrew Gallatin * Transform the Ethernet header into an Ethernet header 2040c792928fSAndrew Gallatin * with 802.1Q encapsulation. 2041c792928fSAndrew Gallatin */ 2042c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 2043c792928fSAndrew Gallatin bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, 2044c792928fSAndrew Gallatin (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); 2045c792928fSAndrew Gallatin evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 2046c792928fSAndrew Gallatin evl->evl_tag = htons(m->m_pkthdr.ether_vtag); 2047c792928fSAndrew Gallatin m->m_flags &= ~M_VLANTAG; 2048c792928fSAndrew Gallatin return m; 2049c792928fSAndrew Gallatin } 205037d89b0cSAndrew Gallatin #endif /* MXGE_NEW_VLAN_API */ 2051c792928fSAndrew Gallatin 2052aed8e389SAndrew Gallatin static void 20531e413cf9SAndrew Gallatin mxge_encap(struct mxge_slice_state *ss, struct mbuf *m) 2054b2fc195eSAndrew Gallatin { 20550a7a780eSAndrew Gallatin struct mxge_pkt_info pi = {0,0,0,0}; 20561e413cf9SAndrew Gallatin mxge_softc_t *sc; 2057b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 2058b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 2059b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 2060b2fc195eSAndrew Gallatin struct ifnet *ifp; 20611e413cf9SAndrew Gallatin mxge_tx_ring_t *tx; 20620a7a780eSAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag; 2063aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 2064aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 2065b2fc195eSAndrew Gallatin 2066b2fc195eSAndrew Gallatin 20671e413cf9SAndrew Gallatin sc = ss->sc; 2068b2fc195eSAndrew Gallatin ifp = sc->ifp; 20691e413cf9SAndrew Gallatin tx = &ss->tx; 2070b2fc195eSAndrew Gallatin 207137d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API 2072c792928fSAndrew Gallatin if (m->m_flags & M_VLANTAG) { 2073c792928fSAndrew Gallatin m = mxge_vlan_tag_insert(m); 2074c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 20750a7a780eSAndrew Gallatin goto drop_without_m; 2076c792928fSAndrew Gallatin } 207737d89b0cSAndrew Gallatin #endif 20780a7a780eSAndrew Gallatin if (m->m_pkthdr.csum_flags & 20790a7a780eSAndrew Gallatin (CSUM_TSO | CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) { 20800a7a780eSAndrew Gallatin if (mxge_parse_tx(ss, m, &pi)) 20810a7a780eSAndrew Gallatin goto drop; 20820a7a780eSAndrew Gallatin } 20830a7a780eSAndrew Gallatin 2084b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 2085b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 2086b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 2087aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 2088b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 2089adae7080SAndrew Gallatin if (__predict_false(err == EFBIG)) { 2090b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 2091b2fc195eSAndrew Gallatin to defrag */ 2092b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 2093b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 2094b2fc195eSAndrew Gallatin goto drop; 2095b2fc195eSAndrew Gallatin } 20961e413cf9SAndrew Gallatin ss->tx.defrag++; 2097b2fc195eSAndrew Gallatin m = m_tmp; 2098b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 2099b2fc195eSAndrew Gallatin tx->info[idx].map, 2100aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 2101b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 2102b2fc195eSAndrew Gallatin } 2103adae7080SAndrew Gallatin if (__predict_false(err != 0)) { 2104aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 2105aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 2106b2fc195eSAndrew Gallatin goto drop; 2107b2fc195eSAndrew Gallatin } 2108b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 2109b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 21105e7d8541SAndrew Gallatin tx->info[idx].m = m; 2111b2fc195eSAndrew Gallatin 211237d89b0cSAndrew Gallatin #if IFCAP_TSO4 2113aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 2114aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 21150a7a780eSAndrew Gallatin mxge_encap_tso(ss, m, cnt, &pi); 2116aed8e389SAndrew Gallatin return; 2117aed8e389SAndrew Gallatin } 211837d89b0cSAndrew Gallatin #endif 2119aed8e389SAndrew Gallatin 2120b2fc195eSAndrew Gallatin req = tx->req_list; 2121b2fc195eSAndrew Gallatin cksum_offset = 0; 21225e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 21235e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 2124b2fc195eSAndrew Gallatin 2125b2fc195eSAndrew Gallatin /* checksum offloading? */ 21260a7a780eSAndrew Gallatin if (m->m_pkthdr.csum_flags & 21270a7a780eSAndrew Gallatin (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) { 2128aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 2129aed8e389SAndrew Gallatin it to a scratch buffer if not */ 21300a7a780eSAndrew Gallatin cksum_offset = pi.ip_off + pi.ip_hlen; 2131b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 21325e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 2133b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 21345e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 2135aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 2136aed8e389SAndrew Gallatin } else { 2137aed8e389SAndrew Gallatin odd_flag = 0; 2138b2fc195eSAndrew Gallatin } 21395e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 21405e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 2141b2fc195eSAndrew Gallatin 2142b2fc195eSAndrew Gallatin /* convert segments into a request list */ 2143b2fc195eSAndrew Gallatin cum_len = 0; 2144aed8e389SAndrew Gallatin seg = tx->seg_list; 21455e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 2146b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 2147b2fc195eSAndrew Gallatin req->addr_low = 21486d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 2149b2fc195eSAndrew Gallatin req->addr_high = 21506d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 2151b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 2152b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 2153b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 2154b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 2155b2fc195eSAndrew Gallatin else 2156b2fc195eSAndrew Gallatin cksum_offset = 0; 21575e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 21585e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 21595e7d8541SAndrew Gallatin req->rdma_count = 1; 2160aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 2161b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 2162b2fc195eSAndrew Gallatin seg++; 2163b2fc195eSAndrew Gallatin req++; 2164b2fc195eSAndrew Gallatin req->flags = 0; 2165b2fc195eSAndrew Gallatin } 2166b2fc195eSAndrew Gallatin req--; 2167b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 2168b2fc195eSAndrew Gallatin if (cum_len < 60) { 2169b2fc195eSAndrew Gallatin req++; 2170b2fc195eSAndrew Gallatin req->addr_low = 21716d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 2172b2fc195eSAndrew Gallatin req->addr_high = 21736d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 2174b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 21755e7d8541SAndrew Gallatin req->cksum_offset = 0; 21765e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 21775e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 21785e7d8541SAndrew Gallatin req->rdma_count = 1; 2179aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 2180b2fc195eSAndrew Gallatin cnt++; 2181b2fc195eSAndrew Gallatin } 21825e7d8541SAndrew Gallatin 21835e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 21845e7d8541SAndrew Gallatin #if 0 21855e7d8541SAndrew Gallatin /* print what the firmware will see */ 21865e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 21875e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 21885e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 21895e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 21905e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 21915e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 21925e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 21935e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 21945e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 21955e7d8541SAndrew Gallatin } 21965e7d8541SAndrew Gallatin printf("--------------\n"); 21975e7d8541SAndrew Gallatin #endif 21985e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 21996d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 2200c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 2201c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && tx->queue_active == 0) { 2202c6cb3e3fSAndrew Gallatin /* tell the NIC to start polling this slice */ 2203c6cb3e3fSAndrew Gallatin *tx->send_go = 1; 2204c6cb3e3fSAndrew Gallatin tx->queue_active = 1; 2205c6cb3e3fSAndrew Gallatin tx->activate++; 2206c6cb3e3fSAndrew Gallatin wmb(); 2207c6cb3e3fSAndrew Gallatin } 2208c6cb3e3fSAndrew Gallatin #endif 2209b2fc195eSAndrew Gallatin return; 2210b2fc195eSAndrew Gallatin 2211b2fc195eSAndrew Gallatin drop: 2212b2fc195eSAndrew Gallatin m_freem(m); 22130a7a780eSAndrew Gallatin drop_without_m: 2214c6cb3e3fSAndrew Gallatin ss->oerrors++; 2215b2fc195eSAndrew Gallatin return; 2216b2fc195eSAndrew Gallatin } 2217b2fc195eSAndrew Gallatin 2218c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 2219c6cb3e3fSAndrew Gallatin static void 2220c6cb3e3fSAndrew Gallatin mxge_qflush(struct ifnet *ifp) 2221c6cb3e3fSAndrew Gallatin { 2222c6cb3e3fSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2223c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx; 2224c6cb3e3fSAndrew Gallatin struct mbuf *m; 2225c6cb3e3fSAndrew Gallatin int slice; 2226b2fc195eSAndrew Gallatin 2227c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 2228c6cb3e3fSAndrew Gallatin tx = &sc->ss[slice].tx; 2229c6cb3e3fSAndrew Gallatin mtx_lock(&tx->mtx); 2230c6cb3e3fSAndrew Gallatin while ((m = buf_ring_dequeue_sc(tx->br)) != NULL) 2231c6cb3e3fSAndrew Gallatin m_freem(m); 2232c6cb3e3fSAndrew Gallatin mtx_unlock(&tx->mtx); 2233c6cb3e3fSAndrew Gallatin } 2234c6cb3e3fSAndrew Gallatin if_qflush(ifp); 2235c6cb3e3fSAndrew Gallatin } 22366d914a32SAndrew Gallatin 2237c6cb3e3fSAndrew Gallatin static inline void 2238c6cb3e3fSAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss) 2239c6cb3e3fSAndrew Gallatin { 2240c6cb3e3fSAndrew Gallatin mxge_softc_t *sc; 2241c6cb3e3fSAndrew Gallatin struct mbuf *m; 2242c6cb3e3fSAndrew Gallatin struct ifnet *ifp; 2243c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx; 2244c6cb3e3fSAndrew Gallatin 2245c6cb3e3fSAndrew Gallatin sc = ss->sc; 2246c6cb3e3fSAndrew Gallatin ifp = sc->ifp; 2247c6cb3e3fSAndrew Gallatin tx = &ss->tx; 2248c6cb3e3fSAndrew Gallatin 2249c6cb3e3fSAndrew Gallatin while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) { 2250c6cb3e3fSAndrew Gallatin m = drbr_dequeue(ifp, tx->br); 2251c6cb3e3fSAndrew Gallatin if (m == NULL) { 2252c6cb3e3fSAndrew Gallatin return; 2253c6cb3e3fSAndrew Gallatin } 2254c6cb3e3fSAndrew Gallatin /* let BPF see it */ 2255c6cb3e3fSAndrew Gallatin BPF_MTAP(ifp, m); 2256c6cb3e3fSAndrew Gallatin 2257c6cb3e3fSAndrew Gallatin /* give it to the nic */ 2258c6cb3e3fSAndrew Gallatin mxge_encap(ss, m); 2259c6cb3e3fSAndrew Gallatin } 2260c6cb3e3fSAndrew Gallatin /* ran out of transmit slots */ 2261c6cb3e3fSAndrew Gallatin if (((ss->if_drv_flags & IFF_DRV_OACTIVE) == 0) 2262c6cb3e3fSAndrew Gallatin && (!drbr_empty(ifp, tx->br))) { 2263c6cb3e3fSAndrew Gallatin ss->if_drv_flags |= IFF_DRV_OACTIVE; 2264c6cb3e3fSAndrew Gallatin tx->stall++; 2265c6cb3e3fSAndrew Gallatin } 2266c6cb3e3fSAndrew Gallatin } 2267c6cb3e3fSAndrew Gallatin 2268c6cb3e3fSAndrew Gallatin static int 2269c6cb3e3fSAndrew Gallatin mxge_transmit_locked(struct mxge_slice_state *ss, struct mbuf *m) 2270c6cb3e3fSAndrew Gallatin { 2271c6cb3e3fSAndrew Gallatin mxge_softc_t *sc; 2272c6cb3e3fSAndrew Gallatin struct ifnet *ifp; 2273c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx; 2274c6cb3e3fSAndrew Gallatin int err; 2275c6cb3e3fSAndrew Gallatin 2276c6cb3e3fSAndrew Gallatin sc = ss->sc; 2277c6cb3e3fSAndrew Gallatin ifp = sc->ifp; 2278c6cb3e3fSAndrew Gallatin tx = &ss->tx; 2279c6cb3e3fSAndrew Gallatin 2280c6cb3e3fSAndrew Gallatin if ((ss->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 2281c6cb3e3fSAndrew Gallatin IFF_DRV_RUNNING) { 2282c6cb3e3fSAndrew Gallatin err = drbr_enqueue(ifp, tx->br, m); 2283c6cb3e3fSAndrew Gallatin return (err); 2284c6cb3e3fSAndrew Gallatin } 2285c6cb3e3fSAndrew Gallatin 2286193cbc4dSMax Laier if (!drbr_needs_enqueue(ifp, tx->br) && 2287c6cb3e3fSAndrew Gallatin ((tx->mask - (tx->req - tx->done)) > tx->max_desc)) { 2288c6cb3e3fSAndrew Gallatin /* let BPF see it */ 2289c6cb3e3fSAndrew Gallatin BPF_MTAP(ifp, m); 2290c6cb3e3fSAndrew Gallatin /* give it to the nic */ 2291c6cb3e3fSAndrew Gallatin mxge_encap(ss, m); 2292c6cb3e3fSAndrew Gallatin } else if ((err = drbr_enqueue(ifp, tx->br, m)) != 0) { 2293c6cb3e3fSAndrew Gallatin return (err); 2294c6cb3e3fSAndrew Gallatin } 2295c6cb3e3fSAndrew Gallatin if (!drbr_empty(ifp, tx->br)) 2296c6cb3e3fSAndrew Gallatin mxge_start_locked(ss); 2297c6cb3e3fSAndrew Gallatin return (0); 2298c6cb3e3fSAndrew Gallatin } 2299c6cb3e3fSAndrew Gallatin 2300c6cb3e3fSAndrew Gallatin static int 2301c6cb3e3fSAndrew Gallatin mxge_transmit(struct ifnet *ifp, struct mbuf *m) 2302c6cb3e3fSAndrew Gallatin { 2303c6cb3e3fSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2304c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss; 2305c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx; 2306c6cb3e3fSAndrew Gallatin int err = 0; 2307c6cb3e3fSAndrew Gallatin int slice; 2308c6cb3e3fSAndrew Gallatin 2309c6cb3e3fSAndrew Gallatin slice = m->m_pkthdr.flowid; 2310c6cb3e3fSAndrew Gallatin slice &= (sc->num_slices - 1); /* num_slices always power of 2 */ 2311c6cb3e3fSAndrew Gallatin 2312c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice]; 2313c6cb3e3fSAndrew Gallatin tx = &ss->tx; 2314c6cb3e3fSAndrew Gallatin 2315c6cb3e3fSAndrew Gallatin if (mtx_trylock(&tx->mtx)) { 2316c6cb3e3fSAndrew Gallatin err = mxge_transmit_locked(ss, m); 2317c6cb3e3fSAndrew Gallatin mtx_unlock(&tx->mtx); 2318c6cb3e3fSAndrew Gallatin } else { 2319c6cb3e3fSAndrew Gallatin err = drbr_enqueue(ifp, tx->br, m); 2320c6cb3e3fSAndrew Gallatin } 2321c6cb3e3fSAndrew Gallatin 2322c6cb3e3fSAndrew Gallatin return (err); 2323c6cb3e3fSAndrew Gallatin } 2324c6cb3e3fSAndrew Gallatin 2325c6cb3e3fSAndrew Gallatin #else 23266d914a32SAndrew Gallatin 23276d914a32SAndrew Gallatin static inline void 23281e413cf9SAndrew Gallatin mxge_start_locked(struct mxge_slice_state *ss) 2329b2fc195eSAndrew Gallatin { 23301e413cf9SAndrew Gallatin mxge_softc_t *sc; 2331b2fc195eSAndrew Gallatin struct mbuf *m; 2332b2fc195eSAndrew Gallatin struct ifnet *ifp; 23331e413cf9SAndrew Gallatin mxge_tx_ring_t *tx; 2334b2fc195eSAndrew Gallatin 23351e413cf9SAndrew Gallatin sc = ss->sc; 2336b2fc195eSAndrew Gallatin ifp = sc->ifp; 23371e413cf9SAndrew Gallatin tx = &ss->tx; 2338adae7080SAndrew Gallatin while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) { 23396d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 23406d914a32SAndrew Gallatin if (m == NULL) { 23416d914a32SAndrew Gallatin return; 23426d914a32SAndrew Gallatin } 2343b2fc195eSAndrew Gallatin /* let BPF see it */ 2344b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 2345b2fc195eSAndrew Gallatin 2346b2fc195eSAndrew Gallatin /* give it to the nic */ 23471e413cf9SAndrew Gallatin mxge_encap(ss, m); 23486d914a32SAndrew Gallatin } 23496d914a32SAndrew Gallatin /* ran out of transmit slots */ 2350a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 2351b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2352adae7080SAndrew Gallatin tx->stall++; 2353a82c2581SAndrew Gallatin } 2354b2fc195eSAndrew Gallatin } 2355c6cb3e3fSAndrew Gallatin #endif 2356b2fc195eSAndrew Gallatin static void 23576d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 2358b2fc195eSAndrew Gallatin { 23596d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 23601e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 2361b2fc195eSAndrew Gallatin 23621e413cf9SAndrew Gallatin /* only use the first slice for now */ 23631e413cf9SAndrew Gallatin ss = &sc->ss[0]; 23641e413cf9SAndrew Gallatin mtx_lock(&ss->tx.mtx); 23651e413cf9SAndrew Gallatin mxge_start_locked(ss); 23661e413cf9SAndrew Gallatin mtx_unlock(&ss->tx.mtx); 2367b2fc195eSAndrew Gallatin } 2368b2fc195eSAndrew Gallatin 23695e7d8541SAndrew Gallatin /* 23705e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 23715e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 23725e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 23735e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 23745e7d8541SAndrew Gallatin * in a burst 23755e7d8541SAndrew Gallatin */ 23765e7d8541SAndrew Gallatin static inline void 23775e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 23785e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 23795e7d8541SAndrew Gallatin { 23805e7d8541SAndrew Gallatin uint32_t low; 23815e7d8541SAndrew Gallatin 23825e7d8541SAndrew Gallatin low = src->addr_low; 23835e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 2384a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 238573c7c83fSAndrew Gallatin wmb(); 2386a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 238773c7c83fSAndrew Gallatin wmb(); 238840385a5fSAndrew Gallatin src->addr_low = low; 23895e7d8541SAndrew Gallatin dst->addr_low = low; 239073c7c83fSAndrew Gallatin wmb(); 23915e7d8541SAndrew Gallatin } 23925e7d8541SAndrew Gallatin 2393b2fc195eSAndrew Gallatin static int 23941e413cf9SAndrew Gallatin mxge_get_buf_small(struct mxge_slice_state *ss, bus_dmamap_t map, int idx) 2395b2fc195eSAndrew Gallatin { 2396b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 2397b2fc195eSAndrew Gallatin struct mbuf *m; 23981e413cf9SAndrew Gallatin mxge_rx_ring_t *rx = &ss->rx_small; 2399b2fc195eSAndrew Gallatin int cnt, err; 2400b2fc195eSAndrew Gallatin 2401c6499eccSGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 2402b2fc195eSAndrew Gallatin if (m == NULL) { 2403b2fc195eSAndrew Gallatin rx->alloc_fail++; 2404b2fc195eSAndrew Gallatin err = ENOBUFS; 2405b2fc195eSAndrew Gallatin goto done; 2406b2fc195eSAndrew Gallatin } 2407b2fc195eSAndrew Gallatin m->m_len = MHLEN; 2408b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 2409b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 2410b2fc195eSAndrew Gallatin if (err != 0) { 2411b2fc195eSAndrew Gallatin m_free(m); 2412b2fc195eSAndrew Gallatin goto done; 2413b2fc195eSAndrew Gallatin } 2414b2fc195eSAndrew Gallatin rx->info[idx].m = m; 2415b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 24166d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 2417b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 24186d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 2419b2fc195eSAndrew Gallatin 2420b2fc195eSAndrew Gallatin done: 2421adae7080SAndrew Gallatin if ((idx & 7) == 7) 2422adae7080SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]); 2423b2fc195eSAndrew Gallatin return err; 2424b2fc195eSAndrew Gallatin } 2425b2fc195eSAndrew Gallatin 2426b2fc195eSAndrew Gallatin static int 24271e413cf9SAndrew Gallatin mxge_get_buf_big(struct mxge_slice_state *ss, bus_dmamap_t map, int idx) 2428b2fc195eSAndrew Gallatin { 2429053e637fSAndrew Gallatin bus_dma_segment_t seg[3]; 2430b2fc195eSAndrew Gallatin struct mbuf *m; 24311e413cf9SAndrew Gallatin mxge_rx_ring_t *rx = &ss->rx_big; 2432053e637fSAndrew Gallatin int cnt, err, i; 2433b2fc195eSAndrew Gallatin 2434c6499eccSGleb Smirnoff m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, rx->cl_size); 2435b2fc195eSAndrew Gallatin if (m == NULL) { 2436b2fc195eSAndrew Gallatin rx->alloc_fail++; 2437b2fc195eSAndrew Gallatin err = ENOBUFS; 2438b2fc195eSAndrew Gallatin goto done; 2439b2fc195eSAndrew Gallatin } 24404d9a5852SAndrew Gallatin m->m_len = rx->mlen; 2441b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 2442053e637fSAndrew Gallatin seg, &cnt, BUS_DMA_NOWAIT); 2443b2fc195eSAndrew Gallatin if (err != 0) { 2444b2fc195eSAndrew Gallatin m_free(m); 2445b2fc195eSAndrew Gallatin goto done; 2446b2fc195eSAndrew Gallatin } 2447b2fc195eSAndrew Gallatin rx->info[idx].m = m; 2448b0f7b922SAndrew Gallatin rx->shadow[idx].addr_low = 2449b0f7b922SAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 2450b0f7b922SAndrew Gallatin rx->shadow[idx].addr_high = 2451b0f7b922SAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 2452053e637fSAndrew Gallatin 2453b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS 2454b0f7b922SAndrew Gallatin for (i = 1; i < cnt; i++) { 2455053e637fSAndrew Gallatin rx->shadow[idx + i].addr_low = 2456053e637fSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr)); 2457053e637fSAndrew Gallatin rx->shadow[idx + i].addr_high = 2458053e637fSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr)); 2459053e637fSAndrew Gallatin } 2460b0f7b922SAndrew Gallatin #endif 2461b2fc195eSAndrew Gallatin 2462b2fc195eSAndrew Gallatin done: 2463053e637fSAndrew Gallatin for (i = 0; i < rx->nbufs; i++) { 2464b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 24655e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 24665e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 2467b2fc195eSAndrew Gallatin } 2468053e637fSAndrew Gallatin idx++; 2469053e637fSAndrew Gallatin } 2470b2fc195eSAndrew Gallatin return err; 2471b2fc195eSAndrew Gallatin } 2472b2fc195eSAndrew Gallatin 247326dd49c6SAndrew Gallatin #ifdef INET6 247426dd49c6SAndrew Gallatin 247526dd49c6SAndrew Gallatin static uint16_t 247626dd49c6SAndrew Gallatin mxge_csum_generic(uint16_t *raw, int len) 247726dd49c6SAndrew Gallatin { 247826dd49c6SAndrew Gallatin uint32_t csum; 247926dd49c6SAndrew Gallatin 248026dd49c6SAndrew Gallatin 248126dd49c6SAndrew Gallatin csum = 0; 248226dd49c6SAndrew Gallatin while (len > 0) { 248326dd49c6SAndrew Gallatin csum += *raw; 248426dd49c6SAndrew Gallatin raw++; 248526dd49c6SAndrew Gallatin len -= 2; 248626dd49c6SAndrew Gallatin } 248726dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xffff); 248826dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xffff); 248926dd49c6SAndrew Gallatin return (uint16_t)csum; 249026dd49c6SAndrew Gallatin } 249126dd49c6SAndrew Gallatin 249226dd49c6SAndrew Gallatin static inline uint16_t 249326dd49c6SAndrew Gallatin mxge_rx_csum6(void *p, struct mbuf *m, uint32_t csum) 249426dd49c6SAndrew Gallatin { 249526dd49c6SAndrew Gallatin uint32_t partial; 249626dd49c6SAndrew Gallatin int nxt, cksum_offset; 249726dd49c6SAndrew Gallatin struct ip6_hdr *ip6 = p; 249826dd49c6SAndrew Gallatin uint16_t c; 249926dd49c6SAndrew Gallatin 250026dd49c6SAndrew Gallatin nxt = ip6->ip6_nxt; 250126dd49c6SAndrew Gallatin cksum_offset = sizeof (*ip6) + ETHER_HDR_LEN; 250226dd49c6SAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) { 250326dd49c6SAndrew Gallatin cksum_offset = ip6_lasthdr(m, ETHER_HDR_LEN, 250426dd49c6SAndrew Gallatin IPPROTO_IPV6, &nxt); 250526dd49c6SAndrew Gallatin if (nxt != IPPROTO_TCP && nxt != IPPROTO_UDP) 250626dd49c6SAndrew Gallatin return (1); 250726dd49c6SAndrew Gallatin } 250826dd49c6SAndrew Gallatin 250926dd49c6SAndrew Gallatin /* 251026dd49c6SAndrew Gallatin * IPv6 headers do not contain a checksum, and hence 251126dd49c6SAndrew Gallatin * do not checksum to zero, so they don't "fall out" 251226dd49c6SAndrew Gallatin * of the partial checksum calculation like IPv4 251326dd49c6SAndrew Gallatin * headers do. We need to fix the partial checksum by 251426dd49c6SAndrew Gallatin * subtracting the checksum of the IPv6 header. 251526dd49c6SAndrew Gallatin */ 251626dd49c6SAndrew Gallatin 251726dd49c6SAndrew Gallatin partial = mxge_csum_generic((uint16_t *)ip6, cksum_offset - 251826dd49c6SAndrew Gallatin ETHER_HDR_LEN); 251926dd49c6SAndrew Gallatin csum += ~partial; 252026dd49c6SAndrew Gallatin csum += (csum < ~partial); 252126dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xFFFF); 252226dd49c6SAndrew Gallatin csum = (csum >> 16) + (csum & 0xFFFF); 252326dd49c6SAndrew Gallatin c = in6_cksum_pseudo(ip6, m->m_pkthdr.len - cksum_offset, nxt, 252426dd49c6SAndrew Gallatin csum); 252526dd49c6SAndrew Gallatin c ^= 0xffff; 252626dd49c6SAndrew Gallatin return (c); 252726dd49c6SAndrew Gallatin } 252826dd49c6SAndrew Gallatin #endif /* INET6 */ 25299b03b0f3SAndrew Gallatin /* 25309b03b0f3SAndrew Gallatin * Myri10GE hardware checksums are not valid if the sender 25319b03b0f3SAndrew Gallatin * padded the frame with non-zero padding. This is because 25329b03b0f3SAndrew Gallatin * the firmware just does a simple 16-bit 1s complement 25339b03b0f3SAndrew Gallatin * checksum across the entire frame, excluding the first 14 2534053e637fSAndrew Gallatin * bytes. It is best to simply to check the checksum and 2535053e637fSAndrew Gallatin * tell the stack about it only if the checksum is good 25369b03b0f3SAndrew Gallatin */ 25379b03b0f3SAndrew Gallatin 2538053e637fSAndrew Gallatin static inline uint16_t 2539053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 2540053e637fSAndrew Gallatin { 2541053e637fSAndrew Gallatin struct ether_header *eh; 254226dd49c6SAndrew Gallatin #ifdef INET 2543053e637fSAndrew Gallatin struct ip *ip; 254426dd49c6SAndrew Gallatin #endif 2545abc5b96bSAndrew Gallatin #if defined(INET) || defined(INET6) 2546abc5b96bSAndrew Gallatin int cap = m->m_pkthdr.rcvif->if_capenable; 2547abc5b96bSAndrew Gallatin #endif 254826dd49c6SAndrew Gallatin uint16_t c, etype; 254926dd49c6SAndrew Gallatin 2550053e637fSAndrew Gallatin 2551053e637fSAndrew Gallatin eh = mtod(m, struct ether_header *); 255226dd49c6SAndrew Gallatin etype = ntohs(eh->ether_type); 255326dd49c6SAndrew Gallatin switch (etype) { 2554eb6219e3SAndrew Gallatin #ifdef INET 255526dd49c6SAndrew Gallatin case ETHERTYPE_IP: 255626dd49c6SAndrew Gallatin if ((cap & IFCAP_RXCSUM) == 0) 255726dd49c6SAndrew Gallatin return (1); 255826dd49c6SAndrew Gallatin ip = (struct ip *)(eh + 1); 255926dd49c6SAndrew Gallatin if (ip->ip_p != IPPROTO_TCP && ip->ip_p != IPPROTO_UDP) 256026dd49c6SAndrew Gallatin return (1); 2561053e637fSAndrew Gallatin c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 256226dd49c6SAndrew Gallatin htonl(ntohs(csum) + ntohs(ip->ip_len) - 256326dd49c6SAndrew Gallatin (ip->ip_hl << 2) + ip->ip_p)); 2564053e637fSAndrew Gallatin c ^= 0xffff; 256526dd49c6SAndrew Gallatin break; 256626dd49c6SAndrew Gallatin #endif 256726dd49c6SAndrew Gallatin #ifdef INET6 256826dd49c6SAndrew Gallatin case ETHERTYPE_IPV6: 256926dd49c6SAndrew Gallatin if ((cap & IFCAP_RXCSUM_IPV6) == 0) 257026dd49c6SAndrew Gallatin return (1); 257126dd49c6SAndrew Gallatin c = mxge_rx_csum6((eh + 1), m, csum); 257226dd49c6SAndrew Gallatin break; 257326dd49c6SAndrew Gallatin #endif 257426dd49c6SAndrew Gallatin default: 257526dd49c6SAndrew Gallatin c = 1; 257626dd49c6SAndrew Gallatin } 2577053e637fSAndrew Gallatin return (c); 25785e7d8541SAndrew Gallatin } 2579053e637fSAndrew Gallatin 2580c792928fSAndrew Gallatin static void 2581c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum) 2582c792928fSAndrew Gallatin { 2583c792928fSAndrew Gallatin struct ether_vlan_header *evl; 2584c792928fSAndrew Gallatin struct ether_header *eh; 2585c792928fSAndrew Gallatin uint32_t partial; 2586c792928fSAndrew Gallatin 2587c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 2588c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2589c792928fSAndrew Gallatin 2590c792928fSAndrew Gallatin /* 2591c792928fSAndrew Gallatin * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes 2592c792928fSAndrew Gallatin * after what the firmware thought was the end of the ethernet 2593c792928fSAndrew Gallatin * header. 2594c792928fSAndrew Gallatin */ 2595c792928fSAndrew Gallatin 2596c792928fSAndrew Gallatin /* put checksum into host byte order */ 2597c792928fSAndrew Gallatin *csum = ntohs(*csum); 2598c792928fSAndrew Gallatin partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN)); 2599c792928fSAndrew Gallatin (*csum) += ~partial; 2600c792928fSAndrew Gallatin (*csum) += ((*csum) < ~partial); 2601c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2602c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2603c792928fSAndrew Gallatin 2604c792928fSAndrew Gallatin /* restore checksum to network byte order; 2605c792928fSAndrew Gallatin later consumers expect this */ 2606c792928fSAndrew Gallatin *csum = htons(*csum); 2607c792928fSAndrew Gallatin 2608c792928fSAndrew Gallatin /* save the tag */ 260937d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API 2610c792928fSAndrew Gallatin m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); 261137d89b0cSAndrew Gallatin #else 261237d89b0cSAndrew Gallatin { 261337d89b0cSAndrew Gallatin struct m_tag *mtag; 261437d89b0cSAndrew Gallatin mtag = m_tag_alloc(MTAG_VLAN, MTAG_VLAN_TAG, sizeof(u_int), 261537d89b0cSAndrew Gallatin M_NOWAIT); 261637d89b0cSAndrew Gallatin if (mtag == NULL) 261737d89b0cSAndrew Gallatin return; 261837d89b0cSAndrew Gallatin VLAN_TAG_VALUE(mtag) = ntohs(evl->evl_tag); 261937d89b0cSAndrew Gallatin m_tag_prepend(m, mtag); 262037d89b0cSAndrew Gallatin } 262137d89b0cSAndrew Gallatin 262237d89b0cSAndrew Gallatin #endif 262337d89b0cSAndrew Gallatin m->m_flags |= M_VLANTAG; 2624c792928fSAndrew Gallatin 2625c792928fSAndrew Gallatin /* 2626c792928fSAndrew Gallatin * Remove the 802.1q header by copying the Ethernet 2627c792928fSAndrew Gallatin * addresses over it and adjusting the beginning of 2628c792928fSAndrew Gallatin * the data in the mbuf. The encapsulated Ethernet 2629c792928fSAndrew Gallatin * type field is already in place. 2630c792928fSAndrew Gallatin */ 2631c792928fSAndrew Gallatin bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, 2632c792928fSAndrew Gallatin ETHER_HDR_LEN - ETHER_TYPE_LEN); 2633c792928fSAndrew Gallatin m_adj(m, ETHER_VLAN_ENCAP_LEN); 2634c792928fSAndrew Gallatin } 2635c792928fSAndrew Gallatin 26365e7d8541SAndrew Gallatin 26375e7d8541SAndrew Gallatin static inline void 263826dd49c6SAndrew Gallatin mxge_rx_done_big(struct mxge_slice_state *ss, uint32_t len, 263926dd49c6SAndrew Gallatin uint32_t csum, int lro) 2640b2fc195eSAndrew Gallatin { 26411e413cf9SAndrew Gallatin mxge_softc_t *sc; 2642b2fc195eSAndrew Gallatin struct ifnet *ifp; 2643053e637fSAndrew Gallatin struct mbuf *m; 2644c792928fSAndrew Gallatin struct ether_header *eh; 26451e413cf9SAndrew Gallatin mxge_rx_ring_t *rx; 2646053e637fSAndrew Gallatin bus_dmamap_t old_map; 2647b2fc195eSAndrew Gallatin int idx; 2648b2fc195eSAndrew Gallatin 26491e413cf9SAndrew Gallatin sc = ss->sc; 2650b2fc195eSAndrew Gallatin ifp = sc->ifp; 26511e413cf9SAndrew Gallatin rx = &ss->rx_big; 2652b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2653053e637fSAndrew Gallatin rx->cnt += rx->nbufs; 2654b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2655b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2656b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 26571e413cf9SAndrew Gallatin if (mxge_get_buf_big(ss, rx->extra_map, idx)) { 2658053e637fSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2659f3f040d9SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 2660053e637fSAndrew Gallatin return; 2661b2fc195eSAndrew Gallatin } 2662053e637fSAndrew Gallatin 2663b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2664b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2665b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2666b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2667b2fc195eSAndrew Gallatin 2668b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2669b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2670b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2671b2fc195eSAndrew Gallatin 2672053e637fSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2673053e637fSAndrew Gallatin * aligned */ 26745e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2675b2fc195eSAndrew Gallatin 2676053e637fSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 2677053e637fSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 26781e413cf9SAndrew Gallatin ss->ipackets++; 2679c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2680c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2681c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2682c792928fSAndrew Gallatin } 2683eacb70baSSepherosa Ziehau /* flowid only valid if RSS hashing is enabled */ 2684eacb70baSSepherosa Ziehau if (sc->num_slices > 1) { 2685eacb70baSSepherosa Ziehau m->m_pkthdr.flowid = (ss - sc->ss); 2686eacb70baSSepherosa Ziehau M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); 2687eacb70baSSepherosa Ziehau } 2688b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 268926dd49c6SAndrew Gallatin if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) && 269026dd49c6SAndrew Gallatin (0 == mxge_rx_csum(m, csum))) { 269126dd49c6SAndrew Gallatin /* Tell the stack that the checksum is good */ 2692053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 269326dd49c6SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | 269426dd49c6SAndrew Gallatin CSUM_DATA_VALID; 269526dd49c6SAndrew Gallatin 269626dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6) 269726dd49c6SAndrew Gallatin if (lro && (0 == tcp_lro_rx(&ss->lc, m, 0))) 269826dd49c6SAndrew Gallatin return; 269926dd49c6SAndrew Gallatin #endif 2700b2fc195eSAndrew Gallatin } 2701053e637fSAndrew Gallatin /* pass the frame up the stack */ 2702053e637fSAndrew Gallatin (*ifp->if_input)(ifp, m); 2703b2fc195eSAndrew Gallatin } 2704b2fc195eSAndrew Gallatin 2705b2fc195eSAndrew Gallatin static inline void 270626dd49c6SAndrew Gallatin mxge_rx_done_small(struct mxge_slice_state *ss, uint32_t len, 270726dd49c6SAndrew Gallatin uint32_t csum, int lro) 2708b2fc195eSAndrew Gallatin { 27091e413cf9SAndrew Gallatin mxge_softc_t *sc; 2710b2fc195eSAndrew Gallatin struct ifnet *ifp; 2711c792928fSAndrew Gallatin struct ether_header *eh; 2712b2fc195eSAndrew Gallatin struct mbuf *m; 27131e413cf9SAndrew Gallatin mxge_rx_ring_t *rx; 2714b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 2715b2fc195eSAndrew Gallatin int idx; 2716b2fc195eSAndrew Gallatin 27171e413cf9SAndrew Gallatin sc = ss->sc; 2718b2fc195eSAndrew Gallatin ifp = sc->ifp; 27191e413cf9SAndrew Gallatin rx = &ss->rx_small; 2720b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2721b2fc195eSAndrew Gallatin rx->cnt++; 2722b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2723b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2724b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 27251e413cf9SAndrew Gallatin if (mxge_get_buf_small(ss, rx->extra_map, idx)) { 2726b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2727f3f040d9SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 2728b2fc195eSAndrew Gallatin return; 2729b2fc195eSAndrew Gallatin } 2730b2fc195eSAndrew Gallatin 2731b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2732b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2733b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2734b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2735b2fc195eSAndrew Gallatin 2736b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2737b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2738b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2739b2fc195eSAndrew Gallatin 2740b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2741b2fc195eSAndrew Gallatin * aligned */ 27425e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2743b2fc195eSAndrew Gallatin 27449b03b0f3SAndrew Gallatin m->m_pkthdr.rcvif = ifp; 27459b03b0f3SAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 27461e413cf9SAndrew Gallatin ss->ipackets++; 2747c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2748c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2749c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2750c792928fSAndrew Gallatin } 2751eacb70baSSepherosa Ziehau /* flowid only valid if RSS hashing is enabled */ 2752eacb70baSSepherosa Ziehau if (sc->num_slices > 1) { 2753eacb70baSSepherosa Ziehau m->m_pkthdr.flowid = (ss - sc->ss); 2754eacb70baSSepherosa Ziehau M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); 2755eacb70baSSepherosa Ziehau } 2756b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 275726dd49c6SAndrew Gallatin if ((ifp->if_capenable & (IFCAP_RXCSUM_IPV6 | IFCAP_RXCSUM)) && 275826dd49c6SAndrew Gallatin (0 == mxge_rx_csum(m, csum))) { 275926dd49c6SAndrew Gallatin /* Tell the stack that the checksum is good */ 2760053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 276126dd49c6SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | 276226dd49c6SAndrew Gallatin CSUM_DATA_VALID; 276326dd49c6SAndrew Gallatin 276426dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6) 276526dd49c6SAndrew Gallatin if (lro && (0 == tcp_lro_rx(&ss->lc, m, csum))) 276626dd49c6SAndrew Gallatin return; 276726dd49c6SAndrew Gallatin #endif 2768053e637fSAndrew Gallatin } 2769b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 2770b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 2771b2fc195eSAndrew Gallatin } 2772b2fc195eSAndrew Gallatin 2773b2fc195eSAndrew Gallatin static inline void 27741e413cf9SAndrew Gallatin mxge_clean_rx_done(struct mxge_slice_state *ss) 27755e7d8541SAndrew Gallatin { 27761e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done = &ss->rx_done; 27775e7d8541SAndrew Gallatin int limit = 0; 27785e7d8541SAndrew Gallatin uint16_t length; 27795e7d8541SAndrew Gallatin uint16_t checksum; 278026dd49c6SAndrew Gallatin int lro; 27815e7d8541SAndrew Gallatin 278226dd49c6SAndrew Gallatin lro = ss->sc->ifp->if_capenable & IFCAP_LRO; 27835e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 27845e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 27855e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 2786053e637fSAndrew Gallatin checksum = rx_done->entry[rx_done->idx].checksum; 2787b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 278826dd49c6SAndrew Gallatin mxge_rx_done_small(ss, length, checksum, lro); 27895e7d8541SAndrew Gallatin else 279026dd49c6SAndrew Gallatin mxge_rx_done_big(ss, length, checksum, lro); 27915e7d8541SAndrew Gallatin rx_done->cnt++; 2792adae7080SAndrew Gallatin rx_done->idx = rx_done->cnt & rx_done->mask; 27935e7d8541SAndrew Gallatin 27945e7d8541SAndrew Gallatin /* limit potential for livelock */ 2795f616ebc7SAndrew Gallatin if (__predict_false(++limit > rx_done->mask / 2)) 27965e7d8541SAndrew Gallatin break; 2797053e637fSAndrew Gallatin } 279826dd49c6SAndrew Gallatin #if defined(INET) || defined (INET6) 27996dd38b87SSepherosa Ziehau tcp_lro_flush_all(&ss->lc); 2800eb6219e3SAndrew Gallatin #endif 28015e7d8541SAndrew Gallatin } 28025e7d8541SAndrew Gallatin 28035e7d8541SAndrew Gallatin 28045e7d8541SAndrew Gallatin static inline void 28051e413cf9SAndrew Gallatin mxge_tx_done(struct mxge_slice_state *ss, uint32_t mcp_idx) 2806b2fc195eSAndrew Gallatin { 2807b2fc195eSAndrew Gallatin struct ifnet *ifp; 28081e413cf9SAndrew Gallatin mxge_tx_ring_t *tx; 2809b2fc195eSAndrew Gallatin struct mbuf *m; 2810b2fc195eSAndrew Gallatin bus_dmamap_t map; 2811f616ebc7SAndrew Gallatin int idx; 2812c6cb3e3fSAndrew Gallatin int *flags; 2813b2fc195eSAndrew Gallatin 28141e413cf9SAndrew Gallatin tx = &ss->tx; 28151e413cf9SAndrew Gallatin ifp = ss->sc->ifp; 28165e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2817b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2818b2fc195eSAndrew Gallatin tx->done++; 2819b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2820b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2821b2fc195eSAndrew Gallatin segment per-mbuf */ 2822b2fc195eSAndrew Gallatin if (m != NULL) { 282371032832SAndrew Gallatin ss->obytes += m->m_pkthdr.len; 282471032832SAndrew Gallatin if (m->m_flags & M_MCAST) 282571032832SAndrew Gallatin ss->omcasts++; 2826c6cb3e3fSAndrew Gallatin ss->opackets++; 2827b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2828b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2829b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2830b2fc195eSAndrew Gallatin m_freem(m); 2831b2fc195eSAndrew Gallatin } 28325e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 28335e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 28345e7d8541SAndrew Gallatin tx->pkt_done++; 28355e7d8541SAndrew Gallatin } 2836b2fc195eSAndrew Gallatin } 2837b2fc195eSAndrew Gallatin 2838b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2839b2fc195eSAndrew Gallatin its OK to send packets */ 2840c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 2841c6cb3e3fSAndrew Gallatin flags = &ss->if_drv_flags; 2842c6cb3e3fSAndrew Gallatin #else 2843c6cb3e3fSAndrew Gallatin flags = &ifp->if_drv_flags; 2844c6cb3e3fSAndrew Gallatin #endif 28451e413cf9SAndrew Gallatin mtx_lock(&ss->tx.mtx); 2846c6cb3e3fSAndrew Gallatin if ((*flags) & IFF_DRV_OACTIVE && 2847c6cb3e3fSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2848c6cb3e3fSAndrew Gallatin *(flags) &= ~IFF_DRV_OACTIVE; 28491e413cf9SAndrew Gallatin ss->tx.wake++; 28501e413cf9SAndrew Gallatin mxge_start_locked(ss); 2851b2fc195eSAndrew Gallatin } 2852c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 2853c6cb3e3fSAndrew Gallatin if ((ss->sc->num_slices > 1) && (tx->req == tx->done)) { 2854c6cb3e3fSAndrew Gallatin /* let the NIC stop polling this queue, since there 2855c6cb3e3fSAndrew Gallatin * are no more transmits pending */ 2856c6cb3e3fSAndrew Gallatin if (tx->req == tx->done) { 2857c6cb3e3fSAndrew Gallatin *tx->send_stop = 1; 2858c6cb3e3fSAndrew Gallatin tx->queue_active = 0; 2859c6cb3e3fSAndrew Gallatin tx->deactivate++; 2860c6cb3e3fSAndrew Gallatin wmb(); 2861c6cb3e3fSAndrew Gallatin } 2862c6cb3e3fSAndrew Gallatin } 2863c6cb3e3fSAndrew Gallatin #endif 2864c6cb3e3fSAndrew Gallatin mtx_unlock(&ss->tx.mtx); 2865c6cb3e3fSAndrew Gallatin 2866b2fc195eSAndrew Gallatin } 2867b2fc195eSAndrew Gallatin 286801638550SAndrew Gallatin static struct mxge_media_type mxge_xfp_media_types[] = 2869c587e59fSAndrew Gallatin { 2870c587e59fSAndrew Gallatin {IFM_10G_CX4, 0x7f, "10GBASE-CX4 (module)"}, 2871c587e59fSAndrew Gallatin {IFM_10G_SR, (1 << 7), "10GBASE-SR"}, 2872c587e59fSAndrew Gallatin {IFM_10G_LR, (1 << 6), "10GBASE-LR"}, 2873c587e59fSAndrew Gallatin {0, (1 << 5), "10GBASE-ER"}, 287401638550SAndrew Gallatin {IFM_10G_LRM, (1 << 4), "10GBASE-LRM"}, 2875c587e59fSAndrew Gallatin {0, (1 << 3), "10GBASE-SW"}, 2876c587e59fSAndrew Gallatin {0, (1 << 2), "10GBASE-LW"}, 2877c587e59fSAndrew Gallatin {0, (1 << 1), "10GBASE-EW"}, 2878c587e59fSAndrew Gallatin {0, (1 << 0), "Reserved"} 2879c587e59fSAndrew Gallatin }; 288001638550SAndrew Gallatin static struct mxge_media_type mxge_sfp_media_types[] = 288101638550SAndrew Gallatin { 288251bc2092SAndrew Gallatin {IFM_10G_TWINAX, 0, "10GBASE-Twinax"}, 28834ae3322fSAndrew Gallatin {0, (1 << 7), "Reserved"}, 288401638550SAndrew Gallatin {IFM_10G_LRM, (1 << 6), "10GBASE-LRM"}, 288501638550SAndrew Gallatin {IFM_10G_LR, (1 << 5), "10GBASE-LR"}, 288656b67858SAndrew Gallatin {IFM_10G_SR, (1 << 4), "10GBASE-SR"}, 288756b67858SAndrew Gallatin {IFM_10G_TWINAX,(1 << 0), "10GBASE-Twinax"} 288801638550SAndrew Gallatin }; 2889c587e59fSAndrew Gallatin 2890c587e59fSAndrew Gallatin static void 2891c406ad2eSAndrew Gallatin mxge_media_set(mxge_softc_t *sc, int media_type) 2892c587e59fSAndrew Gallatin { 2893c406ad2eSAndrew Gallatin 2894c406ad2eSAndrew Gallatin 2895c406ad2eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | media_type, 2896c406ad2eSAndrew Gallatin 0, NULL); 2897c406ad2eSAndrew Gallatin ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | media_type); 2898c406ad2eSAndrew Gallatin sc->current_media = media_type; 2899c406ad2eSAndrew Gallatin sc->media.ifm_media = sc->media.ifm_cur->ifm_media; 2900c587e59fSAndrew Gallatin } 2901c587e59fSAndrew Gallatin 2902c587e59fSAndrew Gallatin static void 2903c406ad2eSAndrew Gallatin mxge_media_init(mxge_softc_t *sc) 2904c587e59fSAndrew Gallatin { 2905c587e59fSAndrew Gallatin char *ptr; 2906c406ad2eSAndrew Gallatin int i; 2907c587e59fSAndrew Gallatin 2908c406ad2eSAndrew Gallatin ifmedia_removeall(&sc->media); 2909c406ad2eSAndrew Gallatin mxge_media_set(sc, IFM_AUTO); 2910c587e59fSAndrew Gallatin 2911c587e59fSAndrew Gallatin /* 2912c587e59fSAndrew Gallatin * parse the product code to deterimine the interface type 2913c587e59fSAndrew Gallatin * (CX4, XFP, Quad Ribbon Fiber) by looking at the character 2914c587e59fSAndrew Gallatin * after the 3rd dash in the driver's cached copy of the 2915c587e59fSAndrew Gallatin * EEPROM's product code string. 2916c587e59fSAndrew Gallatin */ 2917c587e59fSAndrew Gallatin ptr = sc->product_code_string; 2918c587e59fSAndrew Gallatin if (ptr == NULL) { 2919c587e59fSAndrew Gallatin device_printf(sc->dev, "Missing product code\n"); 2920c406ad2eSAndrew Gallatin return; 2921c587e59fSAndrew Gallatin } 2922c587e59fSAndrew Gallatin 2923c587e59fSAndrew Gallatin for (i = 0; i < 3; i++, ptr++) { 2924dc15eac0SEd Schouten ptr = strchr(ptr, '-'); 2925c587e59fSAndrew Gallatin if (ptr == NULL) { 2926c587e59fSAndrew Gallatin device_printf(sc->dev, 2927c587e59fSAndrew Gallatin "only %d dashes in PC?!?\n", i); 2928c587e59fSAndrew Gallatin return; 2929c587e59fSAndrew Gallatin } 2930c587e59fSAndrew Gallatin } 293130882b10SAndrew Gallatin if (*ptr == 'C' || *(ptr +1) == 'C') { 293201638550SAndrew Gallatin /* -C is CX4 */ 2933c406ad2eSAndrew Gallatin sc->connector = MXGE_CX4; 2934c406ad2eSAndrew Gallatin mxge_media_set(sc, IFM_10G_CX4); 2935c406ad2eSAndrew Gallatin } else if (*ptr == 'Q') { 293601638550SAndrew Gallatin /* -Q is Quad Ribbon Fiber */ 2937c406ad2eSAndrew Gallatin sc->connector = MXGE_QRF; 2938c587e59fSAndrew Gallatin device_printf(sc->dev, "Quad Ribbon Fiber Media\n"); 2939c587e59fSAndrew Gallatin /* FreeBSD has no media type for Quad ribbon fiber */ 2940c406ad2eSAndrew Gallatin } else if (*ptr == 'R') { 2941c406ad2eSAndrew Gallatin /* -R is XFP */ 2942c406ad2eSAndrew Gallatin sc->connector = MXGE_XFP; 2943c406ad2eSAndrew Gallatin } else if (*ptr == 'S' || *(ptr +1) == 'S') { 2944c406ad2eSAndrew Gallatin /* -S or -2S is SFP+ */ 2945c406ad2eSAndrew Gallatin sc->connector = MXGE_SFP; 2946c406ad2eSAndrew Gallatin } else { 2947c406ad2eSAndrew Gallatin device_printf(sc->dev, "Unknown media type: %c\n", *ptr); 2948c406ad2eSAndrew Gallatin } 2949c587e59fSAndrew Gallatin } 2950c587e59fSAndrew Gallatin 2951c406ad2eSAndrew Gallatin /* 2952c406ad2eSAndrew Gallatin * Determine the media type for a NIC. Some XFPs will identify 2953c406ad2eSAndrew Gallatin * themselves only when their link is up, so this is initiated via a 2954c406ad2eSAndrew Gallatin * link up interrupt. However, this can potentially take up to 2955c406ad2eSAndrew Gallatin * several milliseconds, so it is run via the watchdog routine, rather 2956c406ad2eSAndrew Gallatin * than in the interrupt handler itself. 2957c406ad2eSAndrew Gallatin */ 2958c406ad2eSAndrew Gallatin static void 2959c406ad2eSAndrew Gallatin mxge_media_probe(mxge_softc_t *sc) 2960c406ad2eSAndrew Gallatin { 2961c406ad2eSAndrew Gallatin mxge_cmd_t cmd; 2962c406ad2eSAndrew Gallatin char *cage_type; 2963c406ad2eSAndrew Gallatin 2964c406ad2eSAndrew Gallatin struct mxge_media_type *mxge_media_types = NULL; 2965c406ad2eSAndrew Gallatin int i, err, ms, mxge_media_type_entries; 2966c406ad2eSAndrew Gallatin uint32_t byte; 2967c406ad2eSAndrew Gallatin 2968c406ad2eSAndrew Gallatin sc->need_media_probe = 0; 2969c406ad2eSAndrew Gallatin 2970c406ad2eSAndrew Gallatin if (sc->connector == MXGE_XFP) { 297101638550SAndrew Gallatin /* -R is XFP */ 297201638550SAndrew Gallatin mxge_media_types = mxge_xfp_media_types; 297301638550SAndrew Gallatin mxge_media_type_entries = 297473a1170aSPedro F. Giffuni nitems(mxge_xfp_media_types); 297501638550SAndrew Gallatin byte = MXGE_XFP_COMPLIANCE_BYTE; 297601638550SAndrew Gallatin cage_type = "XFP"; 2977c406ad2eSAndrew Gallatin } else if (sc->connector == MXGE_SFP) { 297801638550SAndrew Gallatin /* -S or -2S is SFP+ */ 297901638550SAndrew Gallatin mxge_media_types = mxge_sfp_media_types; 298001638550SAndrew Gallatin mxge_media_type_entries = 298173a1170aSPedro F. Giffuni nitems(mxge_sfp_media_types); 298201638550SAndrew Gallatin cage_type = "SFP+"; 298301638550SAndrew Gallatin byte = 3; 2984c406ad2eSAndrew Gallatin } else { 2985c406ad2eSAndrew Gallatin /* nothing to do; media type cannot change */ 2986c587e59fSAndrew Gallatin return; 2987c587e59fSAndrew Gallatin } 2988c587e59fSAndrew Gallatin 2989c587e59fSAndrew Gallatin /* 2990c587e59fSAndrew Gallatin * At this point we know the NIC has an XFP cage, so now we 2991c587e59fSAndrew Gallatin * try to determine what is in the cage by using the 2992c587e59fSAndrew Gallatin * firmware's XFP I2C commands to read the XFP 10GbE compilance 2993c587e59fSAndrew Gallatin * register. We read just one byte, which may take over 2994c587e59fSAndrew Gallatin * a millisecond 2995c587e59fSAndrew Gallatin */ 2996c587e59fSAndrew Gallatin 2997c587e59fSAndrew Gallatin cmd.data0 = 0; /* just fetch 1 byte, not all 256 */ 299801638550SAndrew Gallatin cmd.data1 = byte; 299901638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd); 300001638550SAndrew Gallatin if (err == MXGEFW_CMD_ERROR_I2C_FAILURE) { 3001c587e59fSAndrew Gallatin device_printf(sc->dev, "failed to read XFP\n"); 3002c587e59fSAndrew Gallatin } 300301638550SAndrew Gallatin if (err == MXGEFW_CMD_ERROR_I2C_ABSENT) { 300401638550SAndrew Gallatin device_printf(sc->dev, "Type R/S with no XFP!?!?\n"); 3005c587e59fSAndrew Gallatin } 3006c587e59fSAndrew Gallatin if (err != MXGEFW_CMD_OK) { 3007c587e59fSAndrew Gallatin return; 3008c587e59fSAndrew Gallatin } 3009c587e59fSAndrew Gallatin 3010c587e59fSAndrew Gallatin /* now we wait for the data to be cached */ 301101638550SAndrew Gallatin cmd.data0 = byte; 301201638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd); 3013c587e59fSAndrew Gallatin for (ms = 0; (err == EBUSY) && (ms < 50); ms++) { 3014c587e59fSAndrew Gallatin DELAY(1000); 301501638550SAndrew Gallatin cmd.data0 = byte; 301601638550SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd); 3017c587e59fSAndrew Gallatin } 3018c587e59fSAndrew Gallatin if (err != MXGEFW_CMD_OK) { 301901638550SAndrew Gallatin device_printf(sc->dev, "failed to read %s (%d, %dms)\n", 302001638550SAndrew Gallatin cage_type, err, ms); 3021c587e59fSAndrew Gallatin return; 3022c587e59fSAndrew Gallatin } 3023c587e59fSAndrew Gallatin 3024c587e59fSAndrew Gallatin if (cmd.data0 == mxge_media_types[0].bitmask) { 3025c587e59fSAndrew Gallatin if (mxge_verbose) 302601638550SAndrew Gallatin device_printf(sc->dev, "%s:%s\n", cage_type, 3027c587e59fSAndrew Gallatin mxge_media_types[0].name); 3028c406ad2eSAndrew Gallatin if (sc->current_media != mxge_media_types[0].flag) { 3029c406ad2eSAndrew Gallatin mxge_media_init(sc); 3030c406ad2eSAndrew Gallatin mxge_media_set(sc, mxge_media_types[0].flag); 3031c406ad2eSAndrew Gallatin } 3032c587e59fSAndrew Gallatin return; 3033c587e59fSAndrew Gallatin } 303401638550SAndrew Gallatin for (i = 1; i < mxge_media_type_entries; i++) { 3035c587e59fSAndrew Gallatin if (cmd.data0 & mxge_media_types[i].bitmask) { 3036c587e59fSAndrew Gallatin if (mxge_verbose) 303701638550SAndrew Gallatin device_printf(sc->dev, "%s:%s\n", 303801638550SAndrew Gallatin cage_type, 3039c587e59fSAndrew Gallatin mxge_media_types[i].name); 3040c587e59fSAndrew Gallatin 3041c406ad2eSAndrew Gallatin if (sc->current_media != mxge_media_types[i].flag) { 3042c406ad2eSAndrew Gallatin mxge_media_init(sc); 3043c406ad2eSAndrew Gallatin mxge_media_set(sc, mxge_media_types[i].flag); 3044c406ad2eSAndrew Gallatin } 3045c587e59fSAndrew Gallatin return; 3046c587e59fSAndrew Gallatin } 3047c587e59fSAndrew Gallatin } 3048c406ad2eSAndrew Gallatin if (mxge_verbose) 3049c406ad2eSAndrew Gallatin device_printf(sc->dev, "%s media 0x%x unknown\n", 3050c406ad2eSAndrew Gallatin cage_type, cmd.data0); 3051c587e59fSAndrew Gallatin 3052c587e59fSAndrew Gallatin return; 3053c587e59fSAndrew Gallatin } 3054c587e59fSAndrew Gallatin 3055b2fc195eSAndrew Gallatin static void 30566d87a65dSAndrew Gallatin mxge_intr(void *arg) 3057b2fc195eSAndrew Gallatin { 30581e413cf9SAndrew Gallatin struct mxge_slice_state *ss = arg; 30591e413cf9SAndrew Gallatin mxge_softc_t *sc = ss->sc; 30601e413cf9SAndrew Gallatin mcp_irq_data_t *stats = ss->fw_stats; 30611e413cf9SAndrew Gallatin mxge_tx_ring_t *tx = &ss->tx; 30621e413cf9SAndrew Gallatin mxge_rx_done_t *rx_done = &ss->rx_done; 30635e7d8541SAndrew Gallatin uint32_t send_done_count; 30645e7d8541SAndrew Gallatin uint8_t valid; 3065b2fc195eSAndrew Gallatin 3066b2fc195eSAndrew Gallatin 3067c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 30681e413cf9SAndrew Gallatin /* an interrupt on a non-zero slice is implicitly valid 30691e413cf9SAndrew Gallatin since MSI-X irqs are not shared */ 30701e413cf9SAndrew Gallatin if (ss != sc->ss) { 30711e413cf9SAndrew Gallatin mxge_clean_rx_done(ss); 30721e413cf9SAndrew Gallatin *ss->irq_claim = be32toh(3); 30731e413cf9SAndrew Gallatin return; 30741e413cf9SAndrew Gallatin } 3075c6cb3e3fSAndrew Gallatin #endif 30761e413cf9SAndrew Gallatin 30775e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 30785e7d8541SAndrew Gallatin if (!stats->valid) { 30795e7d8541SAndrew Gallatin return; 3080b2fc195eSAndrew Gallatin } 30815e7d8541SAndrew Gallatin valid = stats->valid; 3082b2fc195eSAndrew Gallatin 308391ed8913SAndrew Gallatin if (sc->legacy_irq) { 30845e7d8541SAndrew Gallatin /* lower legacy IRQ */ 30855e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 30865e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 30875e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 30885e7d8541SAndrew Gallatin stats->valid = 0; 3089dc8731d4SAndrew Gallatin } else { 3090dc8731d4SAndrew Gallatin stats->valid = 0; 3091dc8731d4SAndrew Gallatin } 3092dc8731d4SAndrew Gallatin 3093dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 30945e7d8541SAndrew Gallatin do { 30955e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 30965e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 30975e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 30985e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 3099c6cb3e3fSAndrew Gallatin if (send_done_count != tx->pkt_done) 31001e413cf9SAndrew Gallatin mxge_tx_done(ss, (int)send_done_count); 31011e413cf9SAndrew Gallatin mxge_clean_rx_done(ss); 31025e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 3103b2fc195eSAndrew Gallatin } 310491ed8913SAndrew Gallatin if (sc->legacy_irq && mxge_deassert_wait) 310573c7c83fSAndrew Gallatin wmb(); 31065e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 3107b2fc195eSAndrew Gallatin 3108c6cb3e3fSAndrew Gallatin /* fw link & error stats meaningful only on the first slice */ 3109c6cb3e3fSAndrew Gallatin if (__predict_false((ss == sc->ss) && stats->stats_updated)) { 31105e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 31115e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 3112b2fc195eSAndrew Gallatin if (sc->link_state) { 31135e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 31145e7d8541SAndrew Gallatin if (mxge_verbose) 31155e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 3116b2fc195eSAndrew Gallatin } else { 31175e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 31185e7d8541SAndrew Gallatin if (mxge_verbose) 31195e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 3120b2fc195eSAndrew Gallatin } 3121c587e59fSAndrew Gallatin sc->need_media_probe = 1; 3122b2fc195eSAndrew Gallatin } 3123b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 31241e413cf9SAndrew Gallatin be32toh(stats->rdma_tags_available)) { 3125b2fc195eSAndrew Gallatin sc->rdma_tags_available = 31261e413cf9SAndrew Gallatin be32toh(stats->rdma_tags_available); 31275e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 31285e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 31295e7d8541SAndrew Gallatin } 3130c587e59fSAndrew Gallatin 3131c587e59fSAndrew Gallatin if (stats->link_down) { 31325e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 3133c587e59fSAndrew Gallatin sc->link_state = 0; 3134c587e59fSAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 3135c587e59fSAndrew Gallatin } 3136b2fc195eSAndrew Gallatin } 3137b2fc195eSAndrew Gallatin 31385e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 31395e7d8541SAndrew Gallatin if (valid & 0x1) 31401e413cf9SAndrew Gallatin *ss->irq_claim = be32toh(3); 31411e413cf9SAndrew Gallatin *(ss->irq_claim + 1) = be32toh(3); 3142b2fc195eSAndrew Gallatin } 3143b2fc195eSAndrew Gallatin 3144b2fc195eSAndrew Gallatin static void 31456d87a65dSAndrew Gallatin mxge_init(void *arg) 3146b2fc195eSAndrew Gallatin { 31473cae7311SAndrew Gallatin mxge_softc_t *sc = arg; 31483cae7311SAndrew Gallatin struct ifnet *ifp = sc->ifp; 31493cae7311SAndrew Gallatin 31503cae7311SAndrew Gallatin 31513cae7311SAndrew Gallatin mtx_lock(&sc->driver_mtx); 31523cae7311SAndrew Gallatin if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 31533cae7311SAndrew Gallatin (void) mxge_open(sc); 31543cae7311SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3155b2fc195eSAndrew Gallatin } 3156b2fc195eSAndrew Gallatin 3157b2fc195eSAndrew Gallatin 3158b2fc195eSAndrew Gallatin 3159b2fc195eSAndrew Gallatin static void 31601e413cf9SAndrew Gallatin mxge_free_slice_mbufs(struct mxge_slice_state *ss) 31611e413cf9SAndrew Gallatin { 31621e413cf9SAndrew Gallatin int i; 31631e413cf9SAndrew Gallatin 316426dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6) 316526dd49c6SAndrew Gallatin tcp_lro_free(&ss->lc); 316626dd49c6SAndrew Gallatin #endif 31671e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) { 31681e413cf9SAndrew Gallatin if (ss->rx_big.info[i].m == NULL) 31691e413cf9SAndrew Gallatin continue; 31701e413cf9SAndrew Gallatin bus_dmamap_unload(ss->rx_big.dmat, 31711e413cf9SAndrew Gallatin ss->rx_big.info[i].map); 31721e413cf9SAndrew Gallatin m_freem(ss->rx_big.info[i].m); 31731e413cf9SAndrew Gallatin ss->rx_big.info[i].m = NULL; 31741e413cf9SAndrew Gallatin } 31751e413cf9SAndrew Gallatin 31761e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) { 31771e413cf9SAndrew Gallatin if (ss->rx_small.info[i].m == NULL) 31781e413cf9SAndrew Gallatin continue; 31791e413cf9SAndrew Gallatin bus_dmamap_unload(ss->rx_small.dmat, 31801e413cf9SAndrew Gallatin ss->rx_small.info[i].map); 31811e413cf9SAndrew Gallatin m_freem(ss->rx_small.info[i].m); 31821e413cf9SAndrew Gallatin ss->rx_small.info[i].m = NULL; 31831e413cf9SAndrew Gallatin } 31841e413cf9SAndrew Gallatin 31851e413cf9SAndrew Gallatin /* transmit ring used only on the first slice */ 31861e413cf9SAndrew Gallatin if (ss->tx.info == NULL) 31871e413cf9SAndrew Gallatin return; 31881e413cf9SAndrew Gallatin 31891e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) { 31901e413cf9SAndrew Gallatin ss->tx.info[i].flag = 0; 31911e413cf9SAndrew Gallatin if (ss->tx.info[i].m == NULL) 31921e413cf9SAndrew Gallatin continue; 31931e413cf9SAndrew Gallatin bus_dmamap_unload(ss->tx.dmat, 31941e413cf9SAndrew Gallatin ss->tx.info[i].map); 31951e413cf9SAndrew Gallatin m_freem(ss->tx.info[i].m); 31961e413cf9SAndrew Gallatin ss->tx.info[i].m = NULL; 31971e413cf9SAndrew Gallatin } 31981e413cf9SAndrew Gallatin } 31991e413cf9SAndrew Gallatin 32001e413cf9SAndrew Gallatin static void 32016d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 3202b2fc195eSAndrew Gallatin { 32031e413cf9SAndrew Gallatin int slice; 32041e413cf9SAndrew Gallatin 32051e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) 32061e413cf9SAndrew Gallatin mxge_free_slice_mbufs(&sc->ss[slice]); 32071e413cf9SAndrew Gallatin } 32081e413cf9SAndrew Gallatin 32091e413cf9SAndrew Gallatin static void 32101e413cf9SAndrew Gallatin mxge_free_slice_rings(struct mxge_slice_state *ss) 32111e413cf9SAndrew Gallatin { 3212b2fc195eSAndrew Gallatin int i; 3213b2fc195eSAndrew Gallatin 3214b2fc195eSAndrew Gallatin 32151e413cf9SAndrew Gallatin if (ss->rx_done.entry != NULL) 32161e413cf9SAndrew Gallatin mxge_dma_free(&ss->rx_done.dma); 32171e413cf9SAndrew Gallatin ss->rx_done.entry = NULL; 3218b2fc195eSAndrew Gallatin 32191e413cf9SAndrew Gallatin if (ss->tx.req_bytes != NULL) 32201e413cf9SAndrew Gallatin free(ss->tx.req_bytes, M_DEVBUF); 32211e413cf9SAndrew Gallatin ss->tx.req_bytes = NULL; 32221e413cf9SAndrew Gallatin 32231e413cf9SAndrew Gallatin if (ss->tx.seg_list != NULL) 32241e413cf9SAndrew Gallatin free(ss->tx.seg_list, M_DEVBUF); 32251e413cf9SAndrew Gallatin ss->tx.seg_list = NULL; 32261e413cf9SAndrew Gallatin 32271e413cf9SAndrew Gallatin if (ss->rx_small.shadow != NULL) 32281e413cf9SAndrew Gallatin free(ss->rx_small.shadow, M_DEVBUF); 32291e413cf9SAndrew Gallatin ss->rx_small.shadow = NULL; 32301e413cf9SAndrew Gallatin 32311e413cf9SAndrew Gallatin if (ss->rx_big.shadow != NULL) 32321e413cf9SAndrew Gallatin free(ss->rx_big.shadow, M_DEVBUF); 32331e413cf9SAndrew Gallatin ss->rx_big.shadow = NULL; 32341e413cf9SAndrew Gallatin 32351e413cf9SAndrew Gallatin if (ss->tx.info != NULL) { 32361e413cf9SAndrew Gallatin if (ss->tx.dmat != NULL) { 32371e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) { 32381e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->tx.dmat, 32391e413cf9SAndrew Gallatin ss->tx.info[i].map); 3240b2fc195eSAndrew Gallatin } 32411e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->tx.dmat); 32421e413cf9SAndrew Gallatin } 32431e413cf9SAndrew Gallatin free(ss->tx.info, M_DEVBUF); 32441e413cf9SAndrew Gallatin } 32451e413cf9SAndrew Gallatin ss->tx.info = NULL; 32461e413cf9SAndrew Gallatin 32471e413cf9SAndrew Gallatin if (ss->rx_small.info != NULL) { 32481e413cf9SAndrew Gallatin if (ss->rx_small.dmat != NULL) { 32491e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) { 32501e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_small.dmat, 32511e413cf9SAndrew Gallatin ss->rx_small.info[i].map); 32521e413cf9SAndrew Gallatin } 32531e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_small.dmat, 32541e413cf9SAndrew Gallatin ss->rx_small.extra_map); 32551e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->rx_small.dmat); 32561e413cf9SAndrew Gallatin } 32571e413cf9SAndrew Gallatin free(ss->rx_small.info, M_DEVBUF); 32581e413cf9SAndrew Gallatin } 32591e413cf9SAndrew Gallatin ss->rx_small.info = NULL; 32601e413cf9SAndrew Gallatin 32611e413cf9SAndrew Gallatin if (ss->rx_big.info != NULL) { 32621e413cf9SAndrew Gallatin if (ss->rx_big.dmat != NULL) { 32631e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) { 32641e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_big.dmat, 32651e413cf9SAndrew Gallatin ss->rx_big.info[i].map); 32661e413cf9SAndrew Gallatin } 32671e413cf9SAndrew Gallatin bus_dmamap_destroy(ss->rx_big.dmat, 32681e413cf9SAndrew Gallatin ss->rx_big.extra_map); 32691e413cf9SAndrew Gallatin bus_dma_tag_destroy(ss->rx_big.dmat); 32701e413cf9SAndrew Gallatin } 32711e413cf9SAndrew Gallatin free(ss->rx_big.info, M_DEVBUF); 32721e413cf9SAndrew Gallatin } 32731e413cf9SAndrew Gallatin ss->rx_big.info = NULL; 3274b2fc195eSAndrew Gallatin } 3275b2fc195eSAndrew Gallatin 3276b2fc195eSAndrew Gallatin static void 32776d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 3278b2fc195eSAndrew Gallatin { 32791e413cf9SAndrew Gallatin int slice; 3280b2fc195eSAndrew Gallatin 32811e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) 32821e413cf9SAndrew Gallatin mxge_free_slice_rings(&sc->ss[slice]); 3283c2657176SAndrew Gallatin } 3284b2fc195eSAndrew Gallatin 3285b2fc195eSAndrew Gallatin static int 32861e413cf9SAndrew Gallatin mxge_alloc_slice_rings(struct mxge_slice_state *ss, int rx_ring_entries, 32871e413cf9SAndrew Gallatin int tx_ring_entries) 3288b2fc195eSAndrew Gallatin { 32891e413cf9SAndrew Gallatin mxge_softc_t *sc = ss->sc; 32901e413cf9SAndrew Gallatin size_t bytes; 32911e413cf9SAndrew Gallatin int err, i; 3292b2fc195eSAndrew Gallatin 32931e413cf9SAndrew Gallatin /* allocate per-slice receive resources */ 3294adae7080SAndrew Gallatin 32951e413cf9SAndrew Gallatin ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1; 32961e413cf9SAndrew Gallatin ss->rx_done.mask = (2 * rx_ring_entries) - 1; 3297aed8e389SAndrew Gallatin 3298b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 32991e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_small.shadow); 33001e413cf9SAndrew Gallatin ss->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 3301b2fc195eSAndrew Gallatin 33021e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_big.shadow); 33031e413cf9SAndrew Gallatin ss->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 3304b2fc195eSAndrew Gallatin 33051e413cf9SAndrew Gallatin /* allocate the rx host info rings */ 33061e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_small.info); 33071e413cf9SAndrew Gallatin ss->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 3308b2fc195eSAndrew Gallatin 33091e413cf9SAndrew Gallatin bytes = rx_ring_entries * sizeof (*ss->rx_big.info); 33101e413cf9SAndrew Gallatin ss->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 3311b2fc195eSAndrew Gallatin 33121e413cf9SAndrew Gallatin /* allocate the rx busdma resources */ 3313b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 3314b2fc195eSAndrew Gallatin 1, /* alignment */ 3315b2fc195eSAndrew Gallatin 4096, /* boundary */ 3316b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 3317b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 3318b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 3319b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 3320b2fc195eSAndrew Gallatin 1, /* num segs */ 3321b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 3322b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 3323b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 33241e413cf9SAndrew Gallatin &ss->rx_small.dmat); /* tag */ 3325b2fc195eSAndrew Gallatin if (err != 0) { 3326b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 3327b2fc195eSAndrew Gallatin err); 3328c2ede4b3SMartin Blapp return err; 3329b2fc195eSAndrew Gallatin } 3330b2fc195eSAndrew Gallatin 3331b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 3332b2fc195eSAndrew Gallatin 1, /* alignment */ 3333b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS 3334b2fc195eSAndrew Gallatin 4096, /* boundary */ 3335b0f7b922SAndrew Gallatin #else 3336b0f7b922SAndrew Gallatin 0, /* boundary */ 3337b0f7b922SAndrew Gallatin #endif 3338b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 3339b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 3340b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 3341053e637fSAndrew Gallatin 3*4096, /* maxsize */ 3342b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS 3343053e637fSAndrew Gallatin 3, /* num segs */ 3344b2fc195eSAndrew Gallatin 4096, /* maxsegsize*/ 3345b0f7b922SAndrew Gallatin #else 3346b0f7b922SAndrew Gallatin 1, /* num segs */ 3347b0f7b922SAndrew Gallatin MJUM9BYTES, /* maxsegsize*/ 3348b0f7b922SAndrew Gallatin #endif 3349b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 3350b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 33511e413cf9SAndrew Gallatin &ss->rx_big.dmat); /* tag */ 3352b2fc195eSAndrew Gallatin if (err != 0) { 3353b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 3354b2fc195eSAndrew Gallatin err); 3355c2ede4b3SMartin Blapp return err; 3356b2fc195eSAndrew Gallatin } 33571e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) { 33581e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_small.dmat, 0, 33591e413cf9SAndrew Gallatin &ss->rx_small.info[i].map); 3360b2fc195eSAndrew Gallatin if (err != 0) { 3361b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 3362b2fc195eSAndrew Gallatin err); 3363c2ede4b3SMartin Blapp return err; 3364b2fc195eSAndrew Gallatin } 3365b2fc195eSAndrew Gallatin } 33661e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_small.dmat, 0, 33671e413cf9SAndrew Gallatin &ss->rx_small.extra_map); 3368b2fc195eSAndrew Gallatin if (err != 0) { 3369b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 3370b2fc195eSAndrew Gallatin err); 3371c2ede4b3SMartin Blapp return err; 3372b2fc195eSAndrew Gallatin } 3373b2fc195eSAndrew Gallatin 33741e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) { 33751e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_big.dmat, 0, 33761e413cf9SAndrew Gallatin &ss->rx_big.info[i].map); 3377b2fc195eSAndrew Gallatin if (err != 0) { 3378b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 3379b2fc195eSAndrew Gallatin err); 3380c2ede4b3SMartin Blapp return err; 3381b2fc195eSAndrew Gallatin } 3382b2fc195eSAndrew Gallatin } 33831e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->rx_big.dmat, 0, 33841e413cf9SAndrew Gallatin &ss->rx_big.extra_map); 3385b2fc195eSAndrew Gallatin if (err != 0) { 3386b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 3387b2fc195eSAndrew Gallatin err); 3388c2ede4b3SMartin Blapp return err; 33891e413cf9SAndrew Gallatin } 33901e413cf9SAndrew Gallatin 3391b78540b1SGabor Kovesdan /* now allocate TX resources */ 33921e413cf9SAndrew Gallatin 3393c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 33941e413cf9SAndrew Gallatin /* only use a single TX ring for now */ 33951e413cf9SAndrew Gallatin if (ss != ss->sc->ss) 33961e413cf9SAndrew Gallatin return 0; 3397c6cb3e3fSAndrew Gallatin #endif 33981e413cf9SAndrew Gallatin 33991e413cf9SAndrew Gallatin ss->tx.mask = tx_ring_entries - 1; 34001e413cf9SAndrew Gallatin ss->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4); 34011e413cf9SAndrew Gallatin 34021e413cf9SAndrew Gallatin 34031e413cf9SAndrew Gallatin /* allocate the tx request copy block */ 34041e413cf9SAndrew Gallatin bytes = 8 + 34051e413cf9SAndrew Gallatin sizeof (*ss->tx.req_list) * (ss->tx.max_desc + 4); 34061e413cf9SAndrew Gallatin ss->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 34071e413cf9SAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 34081e413cf9SAndrew Gallatin ss->tx.req_list = (mcp_kreq_ether_send_t *) 34091e413cf9SAndrew Gallatin ((unsigned long)(ss->tx.req_bytes + 7) & ~7UL); 34101e413cf9SAndrew Gallatin 34111e413cf9SAndrew Gallatin /* allocate the tx busdma segment list */ 34121e413cf9SAndrew Gallatin bytes = sizeof (*ss->tx.seg_list) * ss->tx.max_desc; 34131e413cf9SAndrew Gallatin ss->tx.seg_list = (bus_dma_segment_t *) 34141e413cf9SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 34151e413cf9SAndrew Gallatin 34161e413cf9SAndrew Gallatin /* allocate the tx host info ring */ 34171e413cf9SAndrew Gallatin bytes = tx_ring_entries * sizeof (*ss->tx.info); 34181e413cf9SAndrew Gallatin ss->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 34191e413cf9SAndrew Gallatin 34201e413cf9SAndrew Gallatin /* allocate the tx busdma resources */ 34211e413cf9SAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 34221e413cf9SAndrew Gallatin 1, /* alignment */ 34231e413cf9SAndrew Gallatin sc->tx_boundary, /* boundary */ 34241e413cf9SAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 34251e413cf9SAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 34261e413cf9SAndrew Gallatin NULL, NULL, /* filter */ 34271e413cf9SAndrew Gallatin 65536 + 256, /* maxsize */ 34281e413cf9SAndrew Gallatin ss->tx.max_desc - 2, /* num segs */ 34291e413cf9SAndrew Gallatin sc->tx_boundary, /* maxsegsz */ 34301e413cf9SAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 34311e413cf9SAndrew Gallatin NULL, NULL, /* lock */ 34321e413cf9SAndrew Gallatin &ss->tx.dmat); /* tag */ 34331e413cf9SAndrew Gallatin 34341e413cf9SAndrew Gallatin if (err != 0) { 34351e413cf9SAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 34361e413cf9SAndrew Gallatin err); 3437c2ede4b3SMartin Blapp return err; 34381e413cf9SAndrew Gallatin } 34391e413cf9SAndrew Gallatin 34401e413cf9SAndrew Gallatin /* now use these tags to setup dmamaps for each slot 34411e413cf9SAndrew Gallatin in the ring */ 34421e413cf9SAndrew Gallatin for (i = 0; i <= ss->tx.mask; i++) { 34431e413cf9SAndrew Gallatin err = bus_dmamap_create(ss->tx.dmat, 0, 34441e413cf9SAndrew Gallatin &ss->tx.info[i].map); 34451e413cf9SAndrew Gallatin if (err != 0) { 34461e413cf9SAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 34471e413cf9SAndrew Gallatin err); 3448c2ede4b3SMartin Blapp return err; 34491e413cf9SAndrew Gallatin } 3450b2fc195eSAndrew Gallatin } 3451b2fc195eSAndrew Gallatin return 0; 3452b2fc195eSAndrew Gallatin 3453b2fc195eSAndrew Gallatin } 3454b2fc195eSAndrew Gallatin 34551e413cf9SAndrew Gallatin static int 34561e413cf9SAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 34571e413cf9SAndrew Gallatin { 34581e413cf9SAndrew Gallatin mxge_cmd_t cmd; 34591e413cf9SAndrew Gallatin int tx_ring_size; 34601e413cf9SAndrew Gallatin int tx_ring_entries, rx_ring_entries; 34611e413cf9SAndrew Gallatin int err, slice; 34621e413cf9SAndrew Gallatin 34631e413cf9SAndrew Gallatin /* get ring sizes */ 34641e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 34651e413cf9SAndrew Gallatin tx_ring_size = cmd.data0; 34661e413cf9SAndrew Gallatin if (err != 0) { 34671e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine tx ring sizes\n"); 34681e413cf9SAndrew Gallatin goto abort; 34691e413cf9SAndrew Gallatin } 34701e413cf9SAndrew Gallatin 34711e413cf9SAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 34721e413cf9SAndrew Gallatin rx_ring_entries = sc->rx_ring_size / sizeof (mcp_dma_addr_t); 34731e413cf9SAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 34741e413cf9SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 34751e413cf9SAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 34761e413cf9SAndrew Gallatin 34771e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 34781e413cf9SAndrew Gallatin err = mxge_alloc_slice_rings(&sc->ss[slice], 34791e413cf9SAndrew Gallatin rx_ring_entries, 34801e413cf9SAndrew Gallatin tx_ring_entries); 34811e413cf9SAndrew Gallatin if (err != 0) 34821e413cf9SAndrew Gallatin goto abort; 34831e413cf9SAndrew Gallatin } 34841e413cf9SAndrew Gallatin return 0; 34851e413cf9SAndrew Gallatin 34861e413cf9SAndrew Gallatin abort: 34871e413cf9SAndrew Gallatin mxge_free_rings(sc); 34881e413cf9SAndrew Gallatin return err; 34891e413cf9SAndrew Gallatin 34901e413cf9SAndrew Gallatin } 34911e413cf9SAndrew Gallatin 34921e413cf9SAndrew Gallatin 3493053e637fSAndrew Gallatin static void 3494053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs) 3495053e637fSAndrew Gallatin { 3496c792928fSAndrew Gallatin int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD; 3497053e637fSAndrew Gallatin 3498053e637fSAndrew Gallatin if (bufsize < MCLBYTES) { 3499053e637fSAndrew Gallatin /* easy, everything fits in a single buffer */ 3500053e637fSAndrew Gallatin *big_buf_size = MCLBYTES; 3501053e637fSAndrew Gallatin *cl_size = MCLBYTES; 3502053e637fSAndrew Gallatin *nbufs = 1; 3503053e637fSAndrew Gallatin return; 3504053e637fSAndrew Gallatin } 3505053e637fSAndrew Gallatin 3506053e637fSAndrew Gallatin if (bufsize < MJUMPAGESIZE) { 3507053e637fSAndrew Gallatin /* still easy, everything still fits in a single buffer */ 3508053e637fSAndrew Gallatin *big_buf_size = MJUMPAGESIZE; 3509053e637fSAndrew Gallatin *cl_size = MJUMPAGESIZE; 3510053e637fSAndrew Gallatin *nbufs = 1; 3511053e637fSAndrew Gallatin return; 3512053e637fSAndrew Gallatin } 3513b0f7b922SAndrew Gallatin #if MXGE_VIRT_JUMBOS 3514053e637fSAndrew Gallatin /* now we need to use virtually contiguous buffers */ 3515053e637fSAndrew Gallatin *cl_size = MJUM9BYTES; 3516053e637fSAndrew Gallatin *big_buf_size = 4096; 3517053e637fSAndrew Gallatin *nbufs = mtu / 4096 + 1; 3518053e637fSAndrew Gallatin /* needs to be a power of two, so round up */ 3519053e637fSAndrew Gallatin if (*nbufs == 3) 3520053e637fSAndrew Gallatin *nbufs = 4; 3521b0f7b922SAndrew Gallatin #else 3522b0f7b922SAndrew Gallatin *cl_size = MJUM9BYTES; 3523b0f7b922SAndrew Gallatin *big_buf_size = MJUM9BYTES; 3524b0f7b922SAndrew Gallatin *nbufs = 1; 3525b0f7b922SAndrew Gallatin #endif 3526053e637fSAndrew Gallatin } 3527053e637fSAndrew Gallatin 3528b2fc195eSAndrew Gallatin static int 35291e413cf9SAndrew Gallatin mxge_slice_open(struct mxge_slice_state *ss, int nbufs, int cl_size) 3530b2fc195eSAndrew Gallatin { 35311e413cf9SAndrew Gallatin mxge_softc_t *sc; 35326d87a65dSAndrew Gallatin mxge_cmd_t cmd; 3533b2fc195eSAndrew Gallatin bus_dmamap_t map; 35341e413cf9SAndrew Gallatin int err, i, slice; 3535b2fc195eSAndrew Gallatin 35361e413cf9SAndrew Gallatin 35371e413cf9SAndrew Gallatin sc = ss->sc; 35381e413cf9SAndrew Gallatin slice = ss - sc->ss; 35391e413cf9SAndrew Gallatin 354026dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6) 354126dd49c6SAndrew Gallatin (void)tcp_lro_init(&ss->lc); 354226dd49c6SAndrew Gallatin #endif 354326dd49c6SAndrew Gallatin ss->lc.ifp = sc->ifp; 3544053e637fSAndrew Gallatin 35451e413cf9SAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 35461e413cf9SAndrew Gallatin 35471e413cf9SAndrew Gallatin err = 0; 3548c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 35491e413cf9SAndrew Gallatin /* We currently only send from the first slice */ 35501e413cf9SAndrew Gallatin if (slice == 0) { 3551c6cb3e3fSAndrew Gallatin #endif 35521e413cf9SAndrew Gallatin cmd.data0 = slice; 35531e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 35541e413cf9SAndrew Gallatin ss->tx.lanai = 35551e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 3556c6cb3e3fSAndrew Gallatin ss->tx.send_go = (volatile uint32_t *) 3557c6cb3e3fSAndrew Gallatin (sc->sram + MXGEFW_ETH_SEND_GO + 64 * slice); 3558c6cb3e3fSAndrew Gallatin ss->tx.send_stop = (volatile uint32_t *) 3559c6cb3e3fSAndrew Gallatin (sc->sram + MXGEFW_ETH_SEND_STOP + 64 * slice); 3560c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 35611e413cf9SAndrew Gallatin } 3562c6cb3e3fSAndrew Gallatin #endif 35631e413cf9SAndrew Gallatin cmd.data0 = slice; 35641e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc, 35651e413cf9SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 35661e413cf9SAndrew Gallatin ss->rx_small.lanai = 35671e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 35681e413cf9SAndrew Gallatin cmd.data0 = slice; 35691e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 35701e413cf9SAndrew Gallatin ss->rx_big.lanai = 35711e413cf9SAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 35721e413cf9SAndrew Gallatin 35731e413cf9SAndrew Gallatin if (err != 0) { 35741e413cf9SAndrew Gallatin device_printf(sc->dev, 35751e413cf9SAndrew Gallatin "failed to get ring sizes or locations\n"); 35761e413cf9SAndrew Gallatin return EIO; 35771e413cf9SAndrew Gallatin } 35781e413cf9SAndrew Gallatin 35791e413cf9SAndrew Gallatin /* stock receive rings */ 35801e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_small.mask; i++) { 35811e413cf9SAndrew Gallatin map = ss->rx_small.info[i].map; 35821e413cf9SAndrew Gallatin err = mxge_get_buf_small(ss, map, i); 35831e413cf9SAndrew Gallatin if (err) { 35841e413cf9SAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 35851e413cf9SAndrew Gallatin i, ss->rx_small.mask + 1); 35861e413cf9SAndrew Gallatin return ENOMEM; 35871e413cf9SAndrew Gallatin } 35881e413cf9SAndrew Gallatin } 35891e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i++) { 35901e413cf9SAndrew Gallatin ss->rx_big.shadow[i].addr_low = 0xffffffff; 35911e413cf9SAndrew Gallatin ss->rx_big.shadow[i].addr_high = 0xffffffff; 35921e413cf9SAndrew Gallatin } 35931e413cf9SAndrew Gallatin ss->rx_big.nbufs = nbufs; 35941e413cf9SAndrew Gallatin ss->rx_big.cl_size = cl_size; 35954d9a5852SAndrew Gallatin ss->rx_big.mlen = ss->sc->ifp->if_mtu + ETHER_HDR_LEN + 35964d9a5852SAndrew Gallatin ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD; 35971e413cf9SAndrew Gallatin for (i = 0; i <= ss->rx_big.mask; i += ss->rx_big.nbufs) { 35981e413cf9SAndrew Gallatin map = ss->rx_big.info[i].map; 35991e413cf9SAndrew Gallatin err = mxge_get_buf_big(ss, map, i); 36001e413cf9SAndrew Gallatin if (err) { 36011e413cf9SAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 36021e413cf9SAndrew Gallatin i, ss->rx_big.mask + 1); 36031e413cf9SAndrew Gallatin return ENOMEM; 36041e413cf9SAndrew Gallatin } 36051e413cf9SAndrew Gallatin } 36061e413cf9SAndrew Gallatin return 0; 36071e413cf9SAndrew Gallatin } 36081e413cf9SAndrew Gallatin 36091e413cf9SAndrew Gallatin static int 36101e413cf9SAndrew Gallatin mxge_open(mxge_softc_t *sc) 36111e413cf9SAndrew Gallatin { 36121e413cf9SAndrew Gallatin mxge_cmd_t cmd; 36131e413cf9SAndrew Gallatin int err, big_bytes, nbufs, slice, cl_size, i; 36141e413cf9SAndrew Gallatin bus_addr_t bus; 36151e413cf9SAndrew Gallatin volatile uint8_t *itable; 3616c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss; 3617b2fc195eSAndrew Gallatin 36187d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 36197d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 36207d542e2dSAndrew Gallatin 3621adae7080SAndrew Gallatin err = mxge_reset(sc, 1); 3622b2fc195eSAndrew Gallatin if (err != 0) { 3623b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 3624b2fc195eSAndrew Gallatin return EIO; 3625b2fc195eSAndrew Gallatin } 3626b2fc195eSAndrew Gallatin 36271e413cf9SAndrew Gallatin if (sc->num_slices > 1) { 36281e413cf9SAndrew Gallatin /* setup the indirection table */ 36291e413cf9SAndrew Gallatin cmd.data0 = sc->num_slices; 36301e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_TABLE_SIZE, 36311e413cf9SAndrew Gallatin &cmd); 3632b2fc195eSAndrew Gallatin 36331e413cf9SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RSS_TABLE_OFFSET, 36341e413cf9SAndrew Gallatin &cmd); 36351e413cf9SAndrew Gallatin if (err != 0) { 36361e413cf9SAndrew Gallatin device_printf(sc->dev, 36371e413cf9SAndrew Gallatin "failed to setup rss tables\n"); 36381e413cf9SAndrew Gallatin return err; 36391e413cf9SAndrew Gallatin } 36401e413cf9SAndrew Gallatin 36411e413cf9SAndrew Gallatin /* just enable an identity mapping */ 36421e413cf9SAndrew Gallatin itable = sc->sram + cmd.data0; 36431e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) 36441e413cf9SAndrew Gallatin itable[i] = (uint8_t)i; 36451e413cf9SAndrew Gallatin 36461e413cf9SAndrew Gallatin cmd.data0 = 1; 36471e413cf9SAndrew Gallatin cmd.data1 = mxge_rss_hash_type; 36481e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_RSS_ENABLE, &cmd); 36491e413cf9SAndrew Gallatin if (err != 0) { 36501e413cf9SAndrew Gallatin device_printf(sc->dev, "failed to enable slices\n"); 36511e413cf9SAndrew Gallatin return err; 36521e413cf9SAndrew Gallatin } 36531e413cf9SAndrew Gallatin } 36541e413cf9SAndrew Gallatin 36551e413cf9SAndrew Gallatin 36561e413cf9SAndrew Gallatin mxge_choose_params(sc->ifp->if_mtu, &big_bytes, &cl_size, &nbufs); 36571e413cf9SAndrew Gallatin 36581e413cf9SAndrew Gallatin cmd.data0 = nbufs; 3659053e637fSAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 3660053e637fSAndrew Gallatin &cmd); 3661053e637fSAndrew Gallatin /* error is only meaningful if we're trying to set 3662053e637fSAndrew Gallatin MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */ 36631e413cf9SAndrew Gallatin if (err && nbufs > 1) { 3664053e637fSAndrew Gallatin device_printf(sc->dev, 3665053e637fSAndrew Gallatin "Failed to set alway-use-n to %d\n", 36661e413cf9SAndrew Gallatin nbufs); 3667053e637fSAndrew Gallatin return EIO; 3668053e637fSAndrew Gallatin } 3669b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 3670b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 3671b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 3672c792928fSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 36735e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 3674b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 36755e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 3676b2fc195eSAndrew Gallatin &cmd); 3677053e637fSAndrew Gallatin cmd.data0 = big_bytes; 36785e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 36790fa7f681SAndrew Gallatin 36800fa7f681SAndrew Gallatin if (err != 0) { 36810fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 36820fa7f681SAndrew Gallatin goto abort; 36830fa7f681SAndrew Gallatin } 36840fa7f681SAndrew Gallatin 3685b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 3686c6cb3e3fSAndrew Gallatin for (slice = 0; 3687c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 3688c6cb3e3fSAndrew Gallatin slice < sc->num_slices; 3689c6cb3e3fSAndrew Gallatin #else 3690c6cb3e3fSAndrew Gallatin slice < 1; 3691c6cb3e3fSAndrew Gallatin #endif 3692c6cb3e3fSAndrew Gallatin slice++) { 3693c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice]; 3694c6cb3e3fSAndrew Gallatin cmd.data0 = 3695c6cb3e3fSAndrew Gallatin MXGE_LOWPART_TO_U32(ss->fw_stats_dma.bus_addr); 3696c6cb3e3fSAndrew Gallatin cmd.data1 = 3697c6cb3e3fSAndrew Gallatin MXGE_HIGHPART_TO_U32(ss->fw_stats_dma.bus_addr); 36980fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 3699c6cb3e3fSAndrew Gallatin cmd.data2 |= (slice << 16); 3700c6cb3e3fSAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 3701c6cb3e3fSAndrew Gallatin } 37020fa7f681SAndrew Gallatin 37030fa7f681SAndrew Gallatin if (err != 0) { 37041e413cf9SAndrew Gallatin bus = sc->ss->fw_stats_dma.bus_addr; 37050fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 37060fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 37070fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 37080fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 37090fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 37100fa7f681SAndrew Gallatin &cmd); 37110fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 37120fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 37130fa7f681SAndrew Gallatin } else { 37140fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 37150fa7f681SAndrew Gallatin } 3716b2fc195eSAndrew Gallatin 3717b2fc195eSAndrew Gallatin if (err != 0) { 3718b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 3719b2fc195eSAndrew Gallatin goto abort; 3720b2fc195eSAndrew Gallatin } 3721b2fc195eSAndrew Gallatin 37221e413cf9SAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 37231e413cf9SAndrew Gallatin err = mxge_slice_open(&sc->ss[slice], nbufs, cl_size); 37241e413cf9SAndrew Gallatin if (err != 0) { 37251e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't open slice %d\n", 37261e413cf9SAndrew Gallatin slice); 37271e413cf9SAndrew Gallatin goto abort; 37281e413cf9SAndrew Gallatin } 37291e413cf9SAndrew Gallatin } 37301e413cf9SAndrew Gallatin 3731b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 37325e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 3733b2fc195eSAndrew Gallatin if (err) { 3734b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 3735b2fc195eSAndrew Gallatin goto abort; 3736b2fc195eSAndrew Gallatin } 3737c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 3738c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 3739c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice]; 3740c6cb3e3fSAndrew Gallatin ss->if_drv_flags |= IFF_DRV_RUNNING; 3741c6cb3e3fSAndrew Gallatin ss->if_drv_flags &= ~IFF_DRV_OACTIVE; 3742c6cb3e3fSAndrew Gallatin } 3743c6cb3e3fSAndrew Gallatin #endif 3744b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 3745b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 3746b2fc195eSAndrew Gallatin 3747b2fc195eSAndrew Gallatin return 0; 3748b2fc195eSAndrew Gallatin 3749b2fc195eSAndrew Gallatin 3750b2fc195eSAndrew Gallatin abort: 37516d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 3752a98d6cd7SAndrew Gallatin 3753b2fc195eSAndrew Gallatin return err; 3754b2fc195eSAndrew Gallatin } 3755b2fc195eSAndrew Gallatin 3756b2fc195eSAndrew Gallatin static int 3757a393336bSAndrew Gallatin mxge_close(mxge_softc_t *sc, int down) 3758b2fc195eSAndrew Gallatin { 37596d87a65dSAndrew Gallatin mxge_cmd_t cmd; 3760b2fc195eSAndrew Gallatin int err, old_down_cnt; 3761c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 3762c6cb3e3fSAndrew Gallatin struct mxge_slice_state *ss; 3763c6cb3e3fSAndrew Gallatin int slice; 3764c6cb3e3fSAndrew Gallatin #endif 3765b2fc195eSAndrew Gallatin 3766c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 3767c6cb3e3fSAndrew Gallatin for (slice = 0; slice < sc->num_slices; slice++) { 3768c6cb3e3fSAndrew Gallatin ss = &sc->ss[slice]; 3769c6cb3e3fSAndrew Gallatin ss->if_drv_flags &= ~IFF_DRV_RUNNING; 3770c6cb3e3fSAndrew Gallatin } 3771c6cb3e3fSAndrew Gallatin #endif 3772b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 3773a393336bSAndrew Gallatin if (!down) { 3774b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 377573c7c83fSAndrew Gallatin wmb(); 37765e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 3777b2fc195eSAndrew Gallatin if (err) { 3778a393336bSAndrew Gallatin device_printf(sc->dev, 3779a393336bSAndrew Gallatin "Couldn't bring down link\n"); 3780b2fc195eSAndrew Gallatin } 3781b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 3782b2fc195eSAndrew Gallatin /* wait for down irq */ 3783dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 3784b2fc195eSAndrew Gallatin } 378573c7c83fSAndrew Gallatin wmb(); 3786b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 3787b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 3788b2fc195eSAndrew Gallatin } 3789a393336bSAndrew Gallatin } 37906d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 3791a98d6cd7SAndrew Gallatin 3792b2fc195eSAndrew Gallatin return 0; 3793b2fc195eSAndrew Gallatin } 3794b2fc195eSAndrew Gallatin 3795dce01b9bSAndrew Gallatin static void 3796dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 3797dce01b9bSAndrew Gallatin { 3798dce01b9bSAndrew Gallatin device_t dev = sc->dev; 3799dce01b9bSAndrew Gallatin int reg; 3800c68534f1SScott Long uint16_t lnk, pectl; 3801dce01b9bSAndrew Gallatin 3802dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 38033b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { 3804dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 3805dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 3806dce01b9bSAndrew Gallatin 380783d54b59SAndrew Gallatin if (sc->pectl == 0) { 3808dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 3809dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 3810dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 381183d54b59SAndrew Gallatin sc->pectl = pectl; 381283d54b59SAndrew Gallatin } else { 381383d54b59SAndrew Gallatin /* restore saved pectl after watchdog reset */ 381483d54b59SAndrew Gallatin pci_write_config(dev, reg + 0x8, sc->pectl, 2); 381583d54b59SAndrew Gallatin } 3816dce01b9bSAndrew Gallatin } 3817dce01b9bSAndrew Gallatin 3818dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 3819dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 3820dce01b9bSAndrew Gallatin } 3821dce01b9bSAndrew Gallatin 3822dce01b9bSAndrew Gallatin static uint32_t 3823dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 3824dce01b9bSAndrew Gallatin { 3825dce01b9bSAndrew Gallatin device_t dev = sc->dev; 3826dce01b9bSAndrew Gallatin uint32_t vs; 3827dce01b9bSAndrew Gallatin 3828dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 38293b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_VENDOR, &vs) != 0) { 3830dce01b9bSAndrew Gallatin device_printf(sc->dev, 3831dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 3832dce01b9bSAndrew Gallatin return (uint32_t)-1; 3833dce01b9bSAndrew Gallatin } 3834dce01b9bSAndrew Gallatin /* enable read32 mode */ 3835dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 3836dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 3837dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 3838dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 3839dce01b9bSAndrew Gallatin } 3840dce01b9bSAndrew Gallatin 384172c042dfSAndrew Gallatin static void 384272c042dfSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 3843dce01b9bSAndrew Gallatin { 3844e749ef6bSAndrew Gallatin struct pci_devinfo *dinfo; 3845a393336bSAndrew Gallatin struct mxge_slice_state *ss; 3846a393336bSAndrew Gallatin int err, running, s, num_tx_slices = 1; 3847dce01b9bSAndrew Gallatin uint32_t reboot; 3848dce01b9bSAndrew Gallatin uint16_t cmd; 3849dce01b9bSAndrew Gallatin 3850dce01b9bSAndrew Gallatin err = ENXIO; 3851dce01b9bSAndrew Gallatin 3852dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 3853dce01b9bSAndrew Gallatin 3854dce01b9bSAndrew Gallatin /* 3855dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 3856dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 3857dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 3858dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 3859dce01b9bSAndrew Gallatin * again 3860dce01b9bSAndrew Gallatin */ 3861dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 3862dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 3863dce01b9bSAndrew Gallatin /* 3864dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 3865dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 3866dce01b9bSAndrew Gallatin * back, then give up 3867dce01b9bSAndrew Gallatin */ 3868dce01b9bSAndrew Gallatin DELAY(1000*100); 3869dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 3870dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 3871dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 3872dce01b9bSAndrew Gallatin } 3873dce01b9bSAndrew Gallatin } 3874dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 3875dce01b9bSAndrew Gallatin /* print the reboot status */ 3876dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 3877dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 3878dce01b9bSAndrew Gallatin reboot); 3879a393336bSAndrew Gallatin running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING; 3880a393336bSAndrew Gallatin if (running) { 3881a393336bSAndrew Gallatin 3882a393336bSAndrew Gallatin /* 3883a393336bSAndrew Gallatin * quiesce NIC so that TX routines will not try to 3884a393336bSAndrew Gallatin * xmit after restoration of BAR 3885a393336bSAndrew Gallatin */ 3886a393336bSAndrew Gallatin 3887a393336bSAndrew Gallatin /* Mark the link as down */ 3888a393336bSAndrew Gallatin if (sc->link_state) { 3889a393336bSAndrew Gallatin sc->link_state = 0; 3890a393336bSAndrew Gallatin if_link_state_change(sc->ifp, 3891a393336bSAndrew Gallatin LINK_STATE_DOWN); 3892a393336bSAndrew Gallatin } 3893a393336bSAndrew Gallatin #ifdef IFNET_BUF_RING 3894a393336bSAndrew Gallatin num_tx_slices = sc->num_slices; 3895a393336bSAndrew Gallatin #endif 3896a393336bSAndrew Gallatin /* grab all TX locks to ensure no tx */ 3897a393336bSAndrew Gallatin for (s = 0; s < num_tx_slices; s++) { 3898a393336bSAndrew Gallatin ss = &sc->ss[s]; 3899a393336bSAndrew Gallatin mtx_lock(&ss->tx.mtx); 3900a393336bSAndrew Gallatin } 3901a393336bSAndrew Gallatin mxge_close(sc, 1); 3902a393336bSAndrew Gallatin } 3903dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 3904e749ef6bSAndrew Gallatin dinfo = device_get_ivars(sc->dev); 3905e749ef6bSAndrew Gallatin pci_cfg_restore(sc->dev, dinfo); 3906dce01b9bSAndrew Gallatin 3907dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 3908dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 390910882804SAndrew Gallatin 3910a393336bSAndrew Gallatin /* reload f/w */ 3911a393336bSAndrew Gallatin err = mxge_load_firmware(sc, 0); 3912a393336bSAndrew Gallatin if (err) { 3913a393336bSAndrew Gallatin device_printf(sc->dev, 3914a393336bSAndrew Gallatin "Unable to re-load f/w\n"); 391510882804SAndrew Gallatin } 3916a393336bSAndrew Gallatin if (running) { 3917a393336bSAndrew Gallatin if (!err) 3918a393336bSAndrew Gallatin err = mxge_open(sc); 3919a393336bSAndrew Gallatin /* release all TX locks */ 3920a393336bSAndrew Gallatin for (s = 0; s < num_tx_slices; s++) { 3921a393336bSAndrew Gallatin ss = &sc->ss[s]; 392283d54b59SAndrew Gallatin #ifdef IFNET_BUF_RING 392383d54b59SAndrew Gallatin mxge_start_locked(ss); 392483d54b59SAndrew Gallatin #endif 3925a393336bSAndrew Gallatin mtx_unlock(&ss->tx.mtx); 3926a393336bSAndrew Gallatin } 3927a393336bSAndrew Gallatin } 3928a393336bSAndrew Gallatin sc->watchdog_resets++; 3929dce01b9bSAndrew Gallatin } else { 3930c6cb3e3fSAndrew Gallatin device_printf(sc->dev, 393172c042dfSAndrew Gallatin "NIC did not reboot, not resetting\n"); 393272c042dfSAndrew Gallatin err = 0; 393372c042dfSAndrew Gallatin } 393472c042dfSAndrew Gallatin if (err) { 393572c042dfSAndrew Gallatin device_printf(sc->dev, "watchdog reset failed\n"); 393672c042dfSAndrew Gallatin } else { 39376b484a49SAndrew Gallatin if (sc->dying == 2) 39386b484a49SAndrew Gallatin sc->dying = 0; 39396b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 394072c042dfSAndrew Gallatin } 394172c042dfSAndrew Gallatin } 394272c042dfSAndrew Gallatin 394372c042dfSAndrew Gallatin static void 394472c042dfSAndrew Gallatin mxge_watchdog_task(void *arg, int pending) 394572c042dfSAndrew Gallatin { 394672c042dfSAndrew Gallatin mxge_softc_t *sc = arg; 394772c042dfSAndrew Gallatin 394872c042dfSAndrew Gallatin 394972c042dfSAndrew Gallatin mtx_lock(&sc->driver_mtx); 395072c042dfSAndrew Gallatin mxge_watchdog_reset(sc); 395172c042dfSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 395272c042dfSAndrew Gallatin } 395372c042dfSAndrew Gallatin 395472c042dfSAndrew Gallatin static void 395572c042dfSAndrew Gallatin mxge_warn_stuck(mxge_softc_t *sc, mxge_tx_ring_t *tx, int slice) 395672c042dfSAndrew Gallatin { 395772c042dfSAndrew Gallatin tx = &sc->ss[slice].tx; 395872c042dfSAndrew Gallatin device_printf(sc->dev, "slice %d struck? ring state:\n", slice); 3959c6cb3e3fSAndrew Gallatin device_printf(sc->dev, 3960c6cb3e3fSAndrew Gallatin "tx.req=%d tx.done=%d, tx.queue_active=%d\n", 3961c6cb3e3fSAndrew Gallatin tx->req, tx->done, tx->queue_active); 3962c6cb3e3fSAndrew Gallatin device_printf(sc->dev, "tx.activate=%d tx.deactivate=%d\n", 3963c6cb3e3fSAndrew Gallatin tx->activate, tx->deactivate); 3964dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 3965c6cb3e3fSAndrew Gallatin tx->pkt_done, 39661e413cf9SAndrew Gallatin be32toh(sc->ss->fw_stats->send_done_count)); 3967dce01b9bSAndrew Gallatin } 3968dce01b9bSAndrew Gallatin 3969e749ef6bSAndrew Gallatin static int 3970dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 3971dce01b9bSAndrew Gallatin { 3972c6cb3e3fSAndrew Gallatin mxge_tx_ring_t *tx; 39731e413cf9SAndrew Gallatin uint32_t rx_pause = be32toh(sc->ss->fw_stats->dropped_pause); 3974c6cb3e3fSAndrew Gallatin int i, err = 0; 3975dce01b9bSAndrew Gallatin 3976dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 3977dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 3978c6cb3e3fSAndrew Gallatin for (i = 0; 3979c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 3980c6cb3e3fSAndrew Gallatin (i < sc->num_slices) && (err == 0); 3981c6cb3e3fSAndrew Gallatin #else 3982c6cb3e3fSAndrew Gallatin (i < 1) && (err == 0); 3983c6cb3e3fSAndrew Gallatin #endif 3984c6cb3e3fSAndrew Gallatin i++) { 3985c6cb3e3fSAndrew Gallatin tx = &sc->ss[i].tx; 3986dce01b9bSAndrew Gallatin if (tx->req != tx->done && 3987dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 3988c587e59fSAndrew Gallatin tx->done == tx->watchdog_done) { 3989c587e59fSAndrew Gallatin /* check for pause blocking before resetting */ 399072c042dfSAndrew Gallatin if (tx->watchdog_rx_pause == rx_pause) { 399172c042dfSAndrew Gallatin mxge_warn_stuck(sc, tx, i); 399272c042dfSAndrew Gallatin taskqueue_enqueue(sc->tq, &sc->watchdog_task); 399372c042dfSAndrew Gallatin return (ENXIO); 399472c042dfSAndrew Gallatin } 3995c587e59fSAndrew Gallatin else 3996c587e59fSAndrew Gallatin device_printf(sc->dev, "Flow control blocking " 3997c587e59fSAndrew Gallatin "xmits, check link partner\n"); 3998c587e59fSAndrew Gallatin } 3999dce01b9bSAndrew Gallatin 4000dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 4001dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 4002c587e59fSAndrew Gallatin tx->watchdog_rx_pause = rx_pause; 4003c6cb3e3fSAndrew Gallatin } 4004c587e59fSAndrew Gallatin 4005c587e59fSAndrew Gallatin if (sc->need_media_probe) 4006c587e59fSAndrew Gallatin mxge_media_probe(sc); 4007e749ef6bSAndrew Gallatin return (err); 4008dce01b9bSAndrew Gallatin } 4009dce01b9bSAndrew Gallatin 4010f3f040d9SGleb Smirnoff static uint64_t 4011f3f040d9SGleb Smirnoff mxge_get_counter(struct ifnet *ifp, ift_counter cnt) 40121e413cf9SAndrew Gallatin { 4013f3f040d9SGleb Smirnoff struct mxge_softc *sc; 4014f3f040d9SGleb Smirnoff uint64_t rv; 40151e413cf9SAndrew Gallatin 4016f3f040d9SGleb Smirnoff sc = if_getsoftc(ifp); 4017f3f040d9SGleb Smirnoff rv = 0; 4018f3f040d9SGleb Smirnoff 4019f3f040d9SGleb Smirnoff switch (cnt) { 4020f3f040d9SGleb Smirnoff case IFCOUNTER_IPACKETS: 4021f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4022f3f040d9SGleb Smirnoff rv += sc->ss[s].ipackets; 4023f3f040d9SGleb Smirnoff return (rv); 4024f3f040d9SGleb Smirnoff case IFCOUNTER_OPACKETS: 4025f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4026f3f040d9SGleb Smirnoff rv += sc->ss[s].opackets; 4027f3f040d9SGleb Smirnoff return (rv); 4028f3f040d9SGleb Smirnoff case IFCOUNTER_OERRORS: 4029f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4030f3f040d9SGleb Smirnoff rv += sc->ss[s].oerrors; 4031f3f040d9SGleb Smirnoff return (rv); 403271032832SAndrew Gallatin #ifdef IFNET_BUF_RING 4033f3f040d9SGleb Smirnoff case IFCOUNTER_OBYTES: 4034f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4035f3f040d9SGleb Smirnoff rv += sc->ss[s].obytes; 4036f3f040d9SGleb Smirnoff return (rv); 4037f3f040d9SGleb Smirnoff case IFCOUNTER_OMCASTS: 4038f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4039f3f040d9SGleb Smirnoff rv += sc->ss[s].omcasts; 4040f3f040d9SGleb Smirnoff return (rv); 4041f3f040d9SGleb Smirnoff case IFCOUNTER_OQDROPS: 4042f3f040d9SGleb Smirnoff for (int s = 0; s < sc->num_slices; s++) 4043f3f040d9SGleb Smirnoff rv += sc->ss[s].tx.br->br_drops; 4044f3f040d9SGleb Smirnoff return (rv); 404571032832SAndrew Gallatin #endif 4046f3f040d9SGleb Smirnoff default: 4047f3f040d9SGleb Smirnoff return (if_get_counter_default(ifp, cnt)); 40481e413cf9SAndrew Gallatin } 40491e413cf9SAndrew Gallatin } 4050c6cb3e3fSAndrew Gallatin 40511e413cf9SAndrew Gallatin static void 4052dce01b9bSAndrew Gallatin mxge_tick(void *arg) 4053dce01b9bSAndrew Gallatin { 4054dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 40556b484a49SAndrew Gallatin u_long pkts = 0; 4056e749ef6bSAndrew Gallatin int err = 0; 40576b484a49SAndrew Gallatin int running, ticks; 40586b484a49SAndrew Gallatin uint16_t cmd; 4059dce01b9bSAndrew Gallatin 40606b484a49SAndrew Gallatin ticks = mxge_ticks; 40616b484a49SAndrew Gallatin running = sc->ifp->if_drv_flags & IFF_DRV_RUNNING; 40626b484a49SAndrew Gallatin if (running) { 40631e413cf9SAndrew Gallatin if (!sc->watchdog_countdown) { 4064e749ef6bSAndrew Gallatin err = mxge_watchdog(sc); 40651e413cf9SAndrew Gallatin sc->watchdog_countdown = 4; 40661e413cf9SAndrew Gallatin } 40671e413cf9SAndrew Gallatin sc->watchdog_countdown--; 40686b484a49SAndrew Gallatin } 40696b484a49SAndrew Gallatin if (pkts == 0) { 40706b484a49SAndrew Gallatin /* ensure NIC did not suffer h/w fault while idle */ 40716b484a49SAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 40726b484a49SAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 40736b484a49SAndrew Gallatin sc->dying = 2; 40746b484a49SAndrew Gallatin taskqueue_enqueue(sc->tq, &sc->watchdog_task); 40756b484a49SAndrew Gallatin err = ENXIO; 40766b484a49SAndrew Gallatin } 40776b484a49SAndrew Gallatin /* look less often if NIC is idle */ 40786b484a49SAndrew Gallatin ticks *= 4; 40796b484a49SAndrew Gallatin } 40806b484a49SAndrew Gallatin 4081e749ef6bSAndrew Gallatin if (err == 0) 40826b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, ticks, mxge_tick, sc); 4083e749ef6bSAndrew Gallatin 4084dce01b9bSAndrew Gallatin } 4085b2fc195eSAndrew Gallatin 4086b2fc195eSAndrew Gallatin static int 40876d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 4088b2fc195eSAndrew Gallatin { 4089b2fc195eSAndrew Gallatin return EINVAL; 4090b2fc195eSAndrew Gallatin } 4091b2fc195eSAndrew Gallatin 4092b2fc195eSAndrew Gallatin static int 40936d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 4094b2fc195eSAndrew Gallatin { 4095b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 4096b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 4097b2fc195eSAndrew Gallatin int err = 0; 4098b2fc195eSAndrew Gallatin 4099b2fc195eSAndrew Gallatin 4100c792928fSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 4101053e637fSAndrew Gallatin if ((real_mtu > sc->max_mtu) || real_mtu < 60) 4102b2fc195eSAndrew Gallatin return EINVAL; 4103a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 4104b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 4105b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 4106b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 4107a393336bSAndrew Gallatin mxge_close(sc, 0); 41086d87a65dSAndrew Gallatin err = mxge_open(sc); 4109b2fc195eSAndrew Gallatin if (err != 0) { 4110b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 4111a393336bSAndrew Gallatin mxge_close(sc, 0); 41126d87a65dSAndrew Gallatin (void) mxge_open(sc); 4113b2fc195eSAndrew Gallatin } 4114b2fc195eSAndrew Gallatin } 4115a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4116b2fc195eSAndrew Gallatin return err; 4117b2fc195eSAndrew Gallatin } 4118b2fc195eSAndrew Gallatin 4119b2fc195eSAndrew Gallatin static void 41206d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 4121b2fc195eSAndrew Gallatin { 41226d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 4123b2fc195eSAndrew Gallatin 4124b2fc195eSAndrew Gallatin 4125b2fc195eSAndrew Gallatin if (sc == NULL) 4126b2fc195eSAndrew Gallatin return; 4127b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 4128c406ad2eSAndrew Gallatin ifmr->ifm_active = IFM_ETHER | IFM_FDX; 4129c587e59fSAndrew Gallatin ifmr->ifm_status |= sc->link_state ? IFM_ACTIVE : 0; 4130c406ad2eSAndrew Gallatin ifmr->ifm_active |= sc->current_media; 4131b2fc195eSAndrew Gallatin } 4132b2fc195eSAndrew Gallatin 4133b2fc195eSAndrew Gallatin static int 4134df131e84SAndrew Gallatin mxge_fetch_i2c(mxge_softc_t *sc, struct ifi2creq *i2c) 4135df131e84SAndrew Gallatin { 4136df131e84SAndrew Gallatin mxge_cmd_t cmd; 4137df131e84SAndrew Gallatin uint32_t i2c_args; 4138df131e84SAndrew Gallatin int i, ms, err; 4139df131e84SAndrew Gallatin 4140df131e84SAndrew Gallatin 4141df131e84SAndrew Gallatin if (i2c->dev_addr != 0xA0 && 4142df131e84SAndrew Gallatin i2c->dev_addr != 0xA2) 4143df131e84SAndrew Gallatin return (EINVAL); 4144df131e84SAndrew Gallatin if (i2c->len > sizeof(i2c->data)) 4145df131e84SAndrew Gallatin return (EINVAL); 4146df131e84SAndrew Gallatin 4147df131e84SAndrew Gallatin for (i = 0; i < i2c->len; i++) { 4148df131e84SAndrew Gallatin i2c_args = i2c->dev_addr << 0x8; 4149df131e84SAndrew Gallatin i2c_args |= i2c->offset + i; 4150df131e84SAndrew Gallatin cmd.data0 = 0; /* just fetch 1 byte, not all 256 */ 4151df131e84SAndrew Gallatin cmd.data1 = i2c_args; 4152df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_READ, &cmd); 4153df131e84SAndrew Gallatin 4154df131e84SAndrew Gallatin if (err != MXGEFW_CMD_OK) 4155df131e84SAndrew Gallatin return (EIO); 4156df131e84SAndrew Gallatin /* now we wait for the data to be cached */ 4157df131e84SAndrew Gallatin cmd.data0 = i2c_args & 0xff; 4158df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd); 4159df131e84SAndrew Gallatin for (ms = 0; (err == EBUSY) && (ms < 50); ms++) { 4160df131e84SAndrew Gallatin cmd.data0 = i2c_args & 0xff; 4161df131e84SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_I2C_BYTE, &cmd); 4162df131e84SAndrew Gallatin if (err == EBUSY) 4163df131e84SAndrew Gallatin DELAY(1000); 4164df131e84SAndrew Gallatin } 4165df131e84SAndrew Gallatin if (err != MXGEFW_CMD_OK) 4166df131e84SAndrew Gallatin return (EIO); 4167df131e84SAndrew Gallatin i2c->data[i] = cmd.data0; 4168df131e84SAndrew Gallatin } 4169df131e84SAndrew Gallatin return (0); 4170df131e84SAndrew Gallatin } 4171df131e84SAndrew Gallatin 4172df131e84SAndrew Gallatin static int 41736d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 4174b2fc195eSAndrew Gallatin { 41756d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 4176b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 4177df131e84SAndrew Gallatin struct ifi2creq i2c; 4178b2fc195eSAndrew Gallatin int err, mask; 4179b2fc195eSAndrew Gallatin 4180b2fc195eSAndrew Gallatin err = 0; 4181b2fc195eSAndrew Gallatin switch (command) { 4182b2fc195eSAndrew Gallatin case SIOCSIFMTU: 41836d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 4184b2fc195eSAndrew Gallatin break; 4185b2fc195eSAndrew Gallatin 4186b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 4187a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 41888c5d766cSAndrew Gallatin if (sc->dying) { 41898c5d766cSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 41908c5d766cSAndrew Gallatin return EINVAL; 41918c5d766cSAndrew Gallatin } 4192b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 4193dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 41946d87a65dSAndrew Gallatin err = mxge_open(sc); 4195dce01b9bSAndrew Gallatin } else { 41960fa7f681SAndrew Gallatin /* take care of promis can allmulti 41970fa7f681SAndrew Gallatin flag chages */ 41980fa7f681SAndrew Gallatin mxge_change_promisc(sc, 41990fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 42000fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 42010fa7f681SAndrew Gallatin } 4202b2fc195eSAndrew Gallatin } else { 4203dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 4204a393336bSAndrew Gallatin mxge_close(sc, 0); 4205dce01b9bSAndrew Gallatin } 4206b2fc195eSAndrew Gallatin } 4207a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4208b2fc195eSAndrew Gallatin break; 4209b2fc195eSAndrew Gallatin 4210b2fc195eSAndrew Gallatin case SIOCADDMULTI: 4211b2fc195eSAndrew Gallatin case SIOCDELMULTI: 4212a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 4213c0a1f0afSAndrew Gallatin if (sc->dying) { 4214c0a1f0afSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4215c0a1f0afSAndrew Gallatin return (EINVAL); 4216c0a1f0afSAndrew Gallatin } 42170fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 4218a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4219b2fc195eSAndrew Gallatin break; 4220b2fc195eSAndrew Gallatin 4221b2fc195eSAndrew Gallatin case SIOCSIFCAP: 4222a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 4223b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 4224b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 4225b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 4226*cbb9ccf7SRyan Moeller mask &= ~IFCAP_TSO4; 4227aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 42280a7a780eSAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP); 4229b2fc195eSAndrew Gallatin } else { 4230b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 4231b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 4232b2fc195eSAndrew Gallatin } 4233*cbb9ccf7SRyan Moeller } 4234*cbb9ccf7SRyan Moeller if (mask & IFCAP_RXCSUM) { 4235b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 4236b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 4237b2fc195eSAndrew Gallatin } else { 4238b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 4239b2fc195eSAndrew Gallatin } 4240b2fc195eSAndrew Gallatin } 4241aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 4242aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 4243aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 4244aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 4245aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 4246aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 4247aed8e389SAndrew Gallatin } else { 4248aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 4249aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 4250aed8e389SAndrew Gallatin err = EINVAL; 4251aed8e389SAndrew Gallatin } 4252aed8e389SAndrew Gallatin } 42530a7a780eSAndrew Gallatin #if IFCAP_TSO6 42540a7a780eSAndrew Gallatin if (mask & IFCAP_TXCSUM_IPV6) { 42550a7a780eSAndrew Gallatin if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) { 4256*cbb9ccf7SRyan Moeller mask &= ~IFCAP_TSO6; 42570a7a780eSAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 42580a7a780eSAndrew Gallatin | IFCAP_TSO6); 42590a7a780eSAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP_IPV6 42600a7a780eSAndrew Gallatin | CSUM_UDP); 42610a7a780eSAndrew Gallatin } else { 42620a7a780eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM_IPV6; 42630a7a780eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP_IPV6 42640a7a780eSAndrew Gallatin | CSUM_UDP_IPV6); 42650a7a780eSAndrew Gallatin } 4266*cbb9ccf7SRyan Moeller } 4267*cbb9ccf7SRyan Moeller if (mask & IFCAP_RXCSUM_IPV6) { 426826dd49c6SAndrew Gallatin if (IFCAP_RXCSUM_IPV6 & ifp->if_capenable) { 426926dd49c6SAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM_IPV6; 42700a7a780eSAndrew Gallatin } else { 427126dd49c6SAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM_IPV6; 42720a7a780eSAndrew Gallatin } 42730a7a780eSAndrew Gallatin } 42740a7a780eSAndrew Gallatin if (mask & IFCAP_TSO6) { 42750a7a780eSAndrew Gallatin if (IFCAP_TSO6 & ifp->if_capenable) { 42760a7a780eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO6; 42770a7a780eSAndrew Gallatin } else if (IFCAP_TXCSUM_IPV6 & ifp->if_capenable) { 42780a7a780eSAndrew Gallatin ifp->if_capenable |= IFCAP_TSO6; 42790a7a780eSAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 42800a7a780eSAndrew Gallatin } else { 42810a7a780eSAndrew Gallatin printf("mxge requires tx checksum offload" 42820a7a780eSAndrew Gallatin " be enabled to use TSO\n"); 42830a7a780eSAndrew Gallatin err = EINVAL; 42840a7a780eSAndrew Gallatin } 42850a7a780eSAndrew Gallatin } 42860a7a780eSAndrew Gallatin #endif /*IFCAP_TSO6 */ 42870a7a780eSAndrew Gallatin 428826dd49c6SAndrew Gallatin if (mask & IFCAP_LRO) 428926dd49c6SAndrew Gallatin ifp->if_capenable ^= IFCAP_LRO; 4290c792928fSAndrew Gallatin if (mask & IFCAP_VLAN_HWTAGGING) 4291c792928fSAndrew Gallatin ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 42920dce6781SAndrew Gallatin if (mask & IFCAP_VLAN_HWTSO) 42930dce6781SAndrew Gallatin ifp->if_capenable ^= IFCAP_VLAN_HWTSO; 42940dce6781SAndrew Gallatin 42950dce6781SAndrew Gallatin if (!(ifp->if_capabilities & IFCAP_VLAN_HWTSO) || 42960dce6781SAndrew Gallatin !(ifp->if_capenable & IFCAP_VLAN_HWTAGGING)) 42970dce6781SAndrew Gallatin ifp->if_capenable &= ~IFCAP_VLAN_HWTSO; 42980dce6781SAndrew Gallatin 4299a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4300c792928fSAndrew Gallatin VLAN_CAPABILITIES(ifp); 4301c792928fSAndrew Gallatin 4302b2fc195eSAndrew Gallatin break; 4303b2fc195eSAndrew Gallatin 4304b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 4305c406ad2eSAndrew Gallatin mtx_lock(&sc->driver_mtx); 4306c0a1f0afSAndrew Gallatin if (sc->dying) { 4307c0a1f0afSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4308c0a1f0afSAndrew Gallatin return (EINVAL); 4309c0a1f0afSAndrew Gallatin } 4310c406ad2eSAndrew Gallatin mxge_media_probe(sc); 4311c406ad2eSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4312b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 4313b2fc195eSAndrew Gallatin &sc->media, command); 4314b2fc195eSAndrew Gallatin break; 4315b2fc195eSAndrew Gallatin 4316df131e84SAndrew Gallatin case SIOCGI2C: 4317df131e84SAndrew Gallatin if (sc->connector != MXGE_XFP && 4318df131e84SAndrew Gallatin sc->connector != MXGE_SFP) { 4319df131e84SAndrew Gallatin err = ENXIO; 4320df131e84SAndrew Gallatin break; 4321df131e84SAndrew Gallatin } 4322df131e84SAndrew Gallatin err = copyin(ifr_data_get_ptr(ifr), &i2c, sizeof(i2c)); 4323df131e84SAndrew Gallatin if (err != 0) 4324df131e84SAndrew Gallatin break; 4325df131e84SAndrew Gallatin mtx_lock(&sc->driver_mtx); 4326df131e84SAndrew Gallatin if (sc->dying) { 4327df131e84SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4328df131e84SAndrew Gallatin return (EINVAL); 4329df131e84SAndrew Gallatin } 4330df131e84SAndrew Gallatin err = mxge_fetch_i2c(sc, &i2c); 4331df131e84SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 4332df131e84SAndrew Gallatin if (err == 0) 43331b8b041cSBrooks Davis err = copyout(&i2c, ifr_data_get_ptr(ifr), 4334df131e84SAndrew Gallatin sizeof(i2c)); 4335df131e84SAndrew Gallatin break; 4336b2fc195eSAndrew Gallatin default: 4337c756fb6eSRavi Pokala err = ether_ioctl(ifp, command, data); 4338c756fb6eSRavi Pokala break; 4339b2fc195eSAndrew Gallatin } 4340b2fc195eSAndrew Gallatin return err; 4341b2fc195eSAndrew Gallatin } 4342b2fc195eSAndrew Gallatin 4343b2fc195eSAndrew Gallatin static void 43446d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 4345b2fc195eSAndrew Gallatin { 4346b2fc195eSAndrew Gallatin 43471e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.max_slices", &mxge_max_slices); 43486d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 43496d87a65dSAndrew Gallatin &mxge_flow_control); 43506d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 43516d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 43526d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 43536d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 4354d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 4355d91b1b49SAndrew Gallatin &mxge_force_firmware); 43565e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 43575e7d8541SAndrew Gallatin &mxge_deassert_wait); 43585e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 43595e7d8541SAndrew Gallatin &mxge_verbose); 4360dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 43611e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.always_promisc", &mxge_always_promisc); 43621e413cf9SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.rss_hash_type", &mxge_rss_hash_type); 436394c7d993SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.rss_hashtype", &mxge_rss_hash_type); 4364f9453025SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.initial_mtu", &mxge_initial_mtu); 436565c69066SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.throttle", &mxge_throttle); 4366b2fc195eSAndrew Gallatin 43675e7d8541SAndrew Gallatin if (bootverbose) 43685e7d8541SAndrew Gallatin mxge_verbose = 1; 43696d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 43706d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 4371dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 43721e413cf9SAndrew Gallatin mxge_ticks = hz / 2; 43736d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 43741e413cf9SAndrew Gallatin if (mxge_rss_hash_type < MXGEFW_RSS_HASH_TYPE_IPV4 4375bb8ddc66SAndrew Gallatin || mxge_rss_hash_type > MXGEFW_RSS_HASH_TYPE_MAX) { 43765769c5efSAndrew Gallatin mxge_rss_hash_type = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT; 4377b2fc195eSAndrew Gallatin } 4378f9453025SAndrew Gallatin if (mxge_initial_mtu > ETHERMTU_JUMBO || 4379f9453025SAndrew Gallatin mxge_initial_mtu < ETHER_MIN_LEN) 4380f9453025SAndrew Gallatin mxge_initial_mtu = ETHERMTU_JUMBO; 438165c69066SAndrew Gallatin 438265c69066SAndrew Gallatin if (mxge_throttle && mxge_throttle > MXGE_MAX_THROTTLE) 438365c69066SAndrew Gallatin mxge_throttle = MXGE_MAX_THROTTLE; 438465c69066SAndrew Gallatin if (mxge_throttle && mxge_throttle < MXGE_MIN_THROTTLE) 438565c69066SAndrew Gallatin mxge_throttle = MXGE_MIN_THROTTLE; 438665c69066SAndrew Gallatin sc->throttle = mxge_throttle; 43871e413cf9SAndrew Gallatin } 43881e413cf9SAndrew Gallatin 43891e413cf9SAndrew Gallatin 43901e413cf9SAndrew Gallatin static void 43911e413cf9SAndrew Gallatin mxge_free_slices(mxge_softc_t *sc) 43921e413cf9SAndrew Gallatin { 43931e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 43941e413cf9SAndrew Gallatin int i; 43951e413cf9SAndrew Gallatin 43961e413cf9SAndrew Gallatin 43971e413cf9SAndrew Gallatin if (sc->ss == NULL) 43981e413cf9SAndrew Gallatin return; 43991e413cf9SAndrew Gallatin 44001e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 44011e413cf9SAndrew Gallatin ss = &sc->ss[i]; 44021e413cf9SAndrew Gallatin if (ss->fw_stats != NULL) { 44031e413cf9SAndrew Gallatin mxge_dma_free(&ss->fw_stats_dma); 44041e413cf9SAndrew Gallatin ss->fw_stats = NULL; 4405c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 4406c6cb3e3fSAndrew Gallatin if (ss->tx.br != NULL) { 4407c6cb3e3fSAndrew Gallatin drbr_free(ss->tx.br, M_DEVBUF); 4408c6cb3e3fSAndrew Gallatin ss->tx.br = NULL; 4409c6cb3e3fSAndrew Gallatin } 4410c6cb3e3fSAndrew Gallatin #endif 44111e413cf9SAndrew Gallatin mtx_destroy(&ss->tx.mtx); 44121e413cf9SAndrew Gallatin } 44131e413cf9SAndrew Gallatin if (ss->rx_done.entry != NULL) { 44141e413cf9SAndrew Gallatin mxge_dma_free(&ss->rx_done.dma); 44151e413cf9SAndrew Gallatin ss->rx_done.entry = NULL; 44161e413cf9SAndrew Gallatin } 44171e413cf9SAndrew Gallatin } 44181e413cf9SAndrew Gallatin free(sc->ss, M_DEVBUF); 44191e413cf9SAndrew Gallatin sc->ss = NULL; 44201e413cf9SAndrew Gallatin } 44211e413cf9SAndrew Gallatin 44221e413cf9SAndrew Gallatin static int 44231e413cf9SAndrew Gallatin mxge_alloc_slices(mxge_softc_t *sc) 44241e413cf9SAndrew Gallatin { 44251e413cf9SAndrew Gallatin mxge_cmd_t cmd; 44261e413cf9SAndrew Gallatin struct mxge_slice_state *ss; 44271e413cf9SAndrew Gallatin size_t bytes; 44281e413cf9SAndrew Gallatin int err, i, max_intr_slots; 44291e413cf9SAndrew Gallatin 44301e413cf9SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 44311e413cf9SAndrew Gallatin if (err != 0) { 44321e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine rx ring size\n"); 44331e413cf9SAndrew Gallatin return err; 44341e413cf9SAndrew Gallatin } 44351e413cf9SAndrew Gallatin sc->rx_ring_size = cmd.data0; 44361e413cf9SAndrew Gallatin max_intr_slots = 2 * (sc->rx_ring_size / sizeof (mcp_dma_addr_t)); 44371e413cf9SAndrew Gallatin 4438ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->ss) * sc->num_slices; 4439ac2fffa4SPedro F. Giffuni sc->ss = malloc(bytes, M_DEVBUF, M_NOWAIT | M_ZERO); 44401e413cf9SAndrew Gallatin if (sc->ss == NULL) 44411e413cf9SAndrew Gallatin return (ENOMEM); 44421e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 44431e413cf9SAndrew Gallatin ss = &sc->ss[i]; 44441e413cf9SAndrew Gallatin 44451e413cf9SAndrew Gallatin ss->sc = sc; 44461e413cf9SAndrew Gallatin 44471e413cf9SAndrew Gallatin /* allocate per-slice rx interrupt queues */ 44481e413cf9SAndrew Gallatin 44491e413cf9SAndrew Gallatin bytes = max_intr_slots * sizeof (*ss->rx_done.entry); 44501e413cf9SAndrew Gallatin err = mxge_dma_alloc(sc, &ss->rx_done.dma, bytes, 4096); 44511e413cf9SAndrew Gallatin if (err != 0) 44521e413cf9SAndrew Gallatin goto abort; 44531e413cf9SAndrew Gallatin ss->rx_done.entry = ss->rx_done.dma.addr; 44541e413cf9SAndrew Gallatin bzero(ss->rx_done.entry, bytes); 44551e413cf9SAndrew Gallatin 44561e413cf9SAndrew Gallatin /* 44571e413cf9SAndrew Gallatin * allocate the per-slice firmware stats; stats 44581e413cf9SAndrew Gallatin * (including tx) are used used only on the first 44591e413cf9SAndrew Gallatin * slice for now 44601e413cf9SAndrew Gallatin */ 4461c6cb3e3fSAndrew Gallatin #ifndef IFNET_BUF_RING 44621e413cf9SAndrew Gallatin if (i > 0) 44631e413cf9SAndrew Gallatin continue; 4464c6cb3e3fSAndrew Gallatin #endif 44651e413cf9SAndrew Gallatin 44661e413cf9SAndrew Gallatin bytes = sizeof (*ss->fw_stats); 44671e413cf9SAndrew Gallatin err = mxge_dma_alloc(sc, &ss->fw_stats_dma, 44681e413cf9SAndrew Gallatin sizeof (*ss->fw_stats), 64); 44691e413cf9SAndrew Gallatin if (err != 0) 44701e413cf9SAndrew Gallatin goto abort; 44711e413cf9SAndrew Gallatin ss->fw_stats = (mcp_irq_data_t *)ss->fw_stats_dma.addr; 44721e413cf9SAndrew Gallatin snprintf(ss->tx.mtx_name, sizeof(ss->tx.mtx_name), 44731e413cf9SAndrew Gallatin "%s:tx(%d)", device_get_nameunit(sc->dev), i); 44741e413cf9SAndrew Gallatin mtx_init(&ss->tx.mtx, ss->tx.mtx_name, NULL, MTX_DEF); 4475c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 4476c6cb3e3fSAndrew Gallatin ss->tx.br = buf_ring_alloc(2048, M_DEVBUF, M_WAITOK, 4477c6cb3e3fSAndrew Gallatin &ss->tx.mtx); 4478c6cb3e3fSAndrew Gallatin #endif 44791e413cf9SAndrew Gallatin } 44801e413cf9SAndrew Gallatin 44811e413cf9SAndrew Gallatin return (0); 44821e413cf9SAndrew Gallatin 44831e413cf9SAndrew Gallatin abort: 44841e413cf9SAndrew Gallatin mxge_free_slices(sc); 44851e413cf9SAndrew Gallatin return (ENOMEM); 44861e413cf9SAndrew Gallatin } 44871e413cf9SAndrew Gallatin 44881e413cf9SAndrew Gallatin static void 44891e413cf9SAndrew Gallatin mxge_slice_probe(mxge_softc_t *sc) 44901e413cf9SAndrew Gallatin { 44911e413cf9SAndrew Gallatin mxge_cmd_t cmd; 44921e413cf9SAndrew Gallatin char *old_fw; 44931e413cf9SAndrew Gallatin int msix_cnt, status, max_intr_slots; 44941e413cf9SAndrew Gallatin 44951e413cf9SAndrew Gallatin sc->num_slices = 1; 44961e413cf9SAndrew Gallatin /* 44971e413cf9SAndrew Gallatin * don't enable multiple slices if they are not enabled, 44981e413cf9SAndrew Gallatin * or if this is not an SMP system 44991e413cf9SAndrew Gallatin */ 45001e413cf9SAndrew Gallatin 45011e413cf9SAndrew Gallatin if (mxge_max_slices == 0 || mxge_max_slices == 1 || mp_ncpus < 2) 45021e413cf9SAndrew Gallatin return; 45031e413cf9SAndrew Gallatin 45041e413cf9SAndrew Gallatin /* see how many MSI-X interrupts are available */ 45051e413cf9SAndrew Gallatin msix_cnt = pci_msix_count(sc->dev); 45061e413cf9SAndrew Gallatin if (msix_cnt < 2) 45071e413cf9SAndrew Gallatin return; 45081e413cf9SAndrew Gallatin 45091e413cf9SAndrew Gallatin /* now load the slice aware firmware see what it supports */ 45101e413cf9SAndrew Gallatin old_fw = sc->fw_name; 45111e413cf9SAndrew Gallatin if (old_fw == mxge_fw_aligned) 45121e413cf9SAndrew Gallatin sc->fw_name = mxge_fw_rss_aligned; 45131e413cf9SAndrew Gallatin else 45141e413cf9SAndrew Gallatin sc->fw_name = mxge_fw_rss_unaligned; 45151e413cf9SAndrew Gallatin status = mxge_load_firmware(sc, 0); 45161e413cf9SAndrew Gallatin if (status != 0) { 45171e413cf9SAndrew Gallatin device_printf(sc->dev, "Falling back to a single slice\n"); 45181e413cf9SAndrew Gallatin return; 45191e413cf9SAndrew Gallatin } 45201e413cf9SAndrew Gallatin 45211e413cf9SAndrew Gallatin /* try to send a reset command to the card to see if it 45221e413cf9SAndrew Gallatin is alive */ 45231e413cf9SAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 45241e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 45251e413cf9SAndrew Gallatin if (status != 0) { 45261e413cf9SAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 45271e413cf9SAndrew Gallatin goto abort_with_fw; 45281e413cf9SAndrew Gallatin } 45291e413cf9SAndrew Gallatin 45301e413cf9SAndrew Gallatin /* get rx ring size */ 45311e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 45321e413cf9SAndrew Gallatin if (status != 0) { 45331e413cf9SAndrew Gallatin device_printf(sc->dev, "Cannot determine rx ring size\n"); 45341e413cf9SAndrew Gallatin goto abort_with_fw; 45351e413cf9SAndrew Gallatin } 45361e413cf9SAndrew Gallatin max_intr_slots = 2 * (cmd.data0 / sizeof (mcp_dma_addr_t)); 45371e413cf9SAndrew Gallatin 45381e413cf9SAndrew Gallatin /* tell it the size of the interrupt queues */ 45391e413cf9SAndrew Gallatin cmd.data0 = max_intr_slots * sizeof (struct mcp_slot); 45401e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 45411e413cf9SAndrew Gallatin if (status != 0) { 45421e413cf9SAndrew Gallatin device_printf(sc->dev, "failed MXGEFW_CMD_SET_INTRQ_SIZE\n"); 45431e413cf9SAndrew Gallatin goto abort_with_fw; 45441e413cf9SAndrew Gallatin } 45451e413cf9SAndrew Gallatin 45461e413cf9SAndrew Gallatin /* ask the maximum number of slices it supports */ 45471e413cf9SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_RSS_QUEUES, &cmd); 45481e413cf9SAndrew Gallatin if (status != 0) { 45491e413cf9SAndrew Gallatin device_printf(sc->dev, 45501e413cf9SAndrew Gallatin "failed MXGEFW_CMD_GET_MAX_RSS_QUEUES\n"); 45511e413cf9SAndrew Gallatin goto abort_with_fw; 45521e413cf9SAndrew Gallatin } 45531e413cf9SAndrew Gallatin sc->num_slices = cmd.data0; 45541e413cf9SAndrew Gallatin if (sc->num_slices > msix_cnt) 45551e413cf9SAndrew Gallatin sc->num_slices = msix_cnt; 45561e413cf9SAndrew Gallatin 45571e413cf9SAndrew Gallatin if (mxge_max_slices == -1) { 45581e413cf9SAndrew Gallatin /* cap to number of CPUs in system */ 45591e413cf9SAndrew Gallatin if (sc->num_slices > mp_ncpus) 45601e413cf9SAndrew Gallatin sc->num_slices = mp_ncpus; 45611e413cf9SAndrew Gallatin } else { 45621e413cf9SAndrew Gallatin if (sc->num_slices > mxge_max_slices) 45631e413cf9SAndrew Gallatin sc->num_slices = mxge_max_slices; 45641e413cf9SAndrew Gallatin } 45651e413cf9SAndrew Gallatin /* make sure it is a power of two */ 45661e413cf9SAndrew Gallatin while (sc->num_slices & (sc->num_slices - 1)) 45671e413cf9SAndrew Gallatin sc->num_slices--; 45681e413cf9SAndrew Gallatin 45691e413cf9SAndrew Gallatin if (mxge_verbose) 45701e413cf9SAndrew Gallatin device_printf(sc->dev, "using %d slices\n", 45711e413cf9SAndrew Gallatin sc->num_slices); 45721e413cf9SAndrew Gallatin 45731e413cf9SAndrew Gallatin return; 45741e413cf9SAndrew Gallatin 45751e413cf9SAndrew Gallatin abort_with_fw: 45761e413cf9SAndrew Gallatin sc->fw_name = old_fw; 45771e413cf9SAndrew Gallatin (void) mxge_load_firmware(sc, 0); 45781e413cf9SAndrew Gallatin } 45791e413cf9SAndrew Gallatin 45801e413cf9SAndrew Gallatin static int 45811e413cf9SAndrew Gallatin mxge_add_msix_irqs(mxge_softc_t *sc) 45821e413cf9SAndrew Gallatin { 4583ac2fffa4SPedro F. Giffuni size_t bytes; 45841e413cf9SAndrew Gallatin int count, err, i, rid; 45851e413cf9SAndrew Gallatin 45861e413cf9SAndrew Gallatin rid = PCIR_BAR(2); 45871e413cf9SAndrew Gallatin sc->msix_table_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, 45881e413cf9SAndrew Gallatin &rid, RF_ACTIVE); 45891e413cf9SAndrew Gallatin 45901e413cf9SAndrew Gallatin if (sc->msix_table_res == NULL) { 45911e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't alloc MSIX table res\n"); 45921e413cf9SAndrew Gallatin return ENXIO; 45931e413cf9SAndrew Gallatin } 45941e413cf9SAndrew Gallatin 45951e413cf9SAndrew Gallatin count = sc->num_slices; 45961e413cf9SAndrew Gallatin err = pci_alloc_msix(sc->dev, &count); 45971e413cf9SAndrew Gallatin if (err != 0) { 45981e413cf9SAndrew Gallatin device_printf(sc->dev, "pci_alloc_msix: failed, wanted %d" 45991e413cf9SAndrew Gallatin "err = %d \n", sc->num_slices, err); 46001e413cf9SAndrew Gallatin goto abort_with_msix_table; 46011e413cf9SAndrew Gallatin } 46021e413cf9SAndrew Gallatin if (count < sc->num_slices) { 46031e413cf9SAndrew Gallatin device_printf(sc->dev, "pci_alloc_msix: need %d, got %d\n", 46041e413cf9SAndrew Gallatin count, sc->num_slices); 46051e413cf9SAndrew Gallatin device_printf(sc->dev, 46061e413cf9SAndrew Gallatin "Try setting hw.mxge.max_slices to %d\n", 46071e413cf9SAndrew Gallatin count); 46081e413cf9SAndrew Gallatin err = ENOSPC; 46091e413cf9SAndrew Gallatin goto abort_with_msix; 46101e413cf9SAndrew Gallatin } 4611ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->msix_irq_res) * sc->num_slices; 4612ac2fffa4SPedro F. Giffuni sc->msix_irq_res = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO); 46131e413cf9SAndrew Gallatin if (sc->msix_irq_res == NULL) { 46141e413cf9SAndrew Gallatin err = ENOMEM; 46151e413cf9SAndrew Gallatin goto abort_with_msix; 46161e413cf9SAndrew Gallatin } 46171e413cf9SAndrew Gallatin 46181e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 46191e413cf9SAndrew Gallatin rid = i + 1; 46201e413cf9SAndrew Gallatin sc->msix_irq_res[i] = bus_alloc_resource_any(sc->dev, 46211e413cf9SAndrew Gallatin SYS_RES_IRQ, 46221e413cf9SAndrew Gallatin &rid, RF_ACTIVE); 46231e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] == NULL) { 46241e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't allocate IRQ res" 46251e413cf9SAndrew Gallatin " for message %d\n", i); 46261e413cf9SAndrew Gallatin err = ENXIO; 46271e413cf9SAndrew Gallatin goto abort_with_res; 46281e413cf9SAndrew Gallatin } 46291e413cf9SAndrew Gallatin } 46301e413cf9SAndrew Gallatin 4631ac2fffa4SPedro F. Giffuni bytes = sizeof (*sc->msix_ih) * sc->num_slices; 4632ac2fffa4SPedro F. Giffuni sc->msix_ih = malloc(bytes, M_DEVBUF, M_NOWAIT|M_ZERO); 46331e413cf9SAndrew Gallatin 46341e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 46351e413cf9SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->msix_irq_res[i], 46361e413cf9SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 463737d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030 463837d89b0cSAndrew Gallatin NULL, 463937d89b0cSAndrew Gallatin #endif 464037d89b0cSAndrew Gallatin mxge_intr, &sc->ss[i], &sc->msix_ih[i]); 46411e413cf9SAndrew Gallatin if (err != 0) { 46421e413cf9SAndrew Gallatin device_printf(sc->dev, "couldn't setup intr for " 46431e413cf9SAndrew Gallatin "message %d\n", i); 46441e413cf9SAndrew Gallatin goto abort_with_intr; 46451e413cf9SAndrew Gallatin } 464621089137SAndrew Gallatin bus_describe_intr(sc->dev, sc->msix_irq_res[i], 464721089137SAndrew Gallatin sc->msix_ih[i], "s%d", i); 46481e413cf9SAndrew Gallatin } 46491e413cf9SAndrew Gallatin 46501e413cf9SAndrew Gallatin if (mxge_verbose) { 46511e413cf9SAndrew Gallatin device_printf(sc->dev, "using %d msix IRQs:", 46521e413cf9SAndrew Gallatin sc->num_slices); 46531e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) 4654da1b038aSJustin Hibbits printf(" %jd", rman_get_start(sc->msix_irq_res[i])); 46551e413cf9SAndrew Gallatin printf("\n"); 46561e413cf9SAndrew Gallatin } 46571e413cf9SAndrew Gallatin return (0); 46581e413cf9SAndrew Gallatin 46591e413cf9SAndrew Gallatin abort_with_intr: 46601e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 46611e413cf9SAndrew Gallatin if (sc->msix_ih[i] != NULL) { 46621e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->msix_irq_res[i], 46631e413cf9SAndrew Gallatin sc->msix_ih[i]); 46641e413cf9SAndrew Gallatin sc->msix_ih[i] = NULL; 46651e413cf9SAndrew Gallatin } 46661e413cf9SAndrew Gallatin } 46671e413cf9SAndrew Gallatin free(sc->msix_ih, M_DEVBUF); 46681e413cf9SAndrew Gallatin 46691e413cf9SAndrew Gallatin 46701e413cf9SAndrew Gallatin abort_with_res: 46711e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 46721e413cf9SAndrew Gallatin rid = i + 1; 46731e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] != NULL) 46741e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, rid, 46751e413cf9SAndrew Gallatin sc->msix_irq_res[i]); 46761e413cf9SAndrew Gallatin sc->msix_irq_res[i] = NULL; 46771e413cf9SAndrew Gallatin } 46781e413cf9SAndrew Gallatin free(sc->msix_irq_res, M_DEVBUF); 46791e413cf9SAndrew Gallatin 46801e413cf9SAndrew Gallatin 46811e413cf9SAndrew Gallatin abort_with_msix: 46821e413cf9SAndrew Gallatin pci_release_msi(sc->dev); 46831e413cf9SAndrew Gallatin 46841e413cf9SAndrew Gallatin abort_with_msix_table: 46851e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2), 46861e413cf9SAndrew Gallatin sc->msix_table_res); 46871e413cf9SAndrew Gallatin 46881e413cf9SAndrew Gallatin return err; 46891e413cf9SAndrew Gallatin } 46901e413cf9SAndrew Gallatin 46911e413cf9SAndrew Gallatin static int 46921e413cf9SAndrew Gallatin mxge_add_single_irq(mxge_softc_t *sc) 46931e413cf9SAndrew Gallatin { 46941e413cf9SAndrew Gallatin int count, err, rid; 46951e413cf9SAndrew Gallatin 46961e413cf9SAndrew Gallatin count = pci_msi_count(sc->dev); 46971e413cf9SAndrew Gallatin if (count == 1 && pci_alloc_msi(sc->dev, &count) == 0) { 46981e413cf9SAndrew Gallatin rid = 1; 46991e413cf9SAndrew Gallatin } else { 47001e413cf9SAndrew Gallatin rid = 0; 470191ed8913SAndrew Gallatin sc->legacy_irq = 1; 47021e413cf9SAndrew Gallatin } 470343cd6160SJustin Hibbits sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, 470443cd6160SJustin Hibbits RF_SHAREABLE | RF_ACTIVE); 47051e413cf9SAndrew Gallatin if (sc->irq_res == NULL) { 47061e413cf9SAndrew Gallatin device_printf(sc->dev, "could not alloc interrupt\n"); 47071e413cf9SAndrew Gallatin return ENXIO; 47081e413cf9SAndrew Gallatin } 47091e413cf9SAndrew Gallatin if (mxge_verbose) 4710da1b038aSJustin Hibbits device_printf(sc->dev, "using %s irq %jd\n", 471191ed8913SAndrew Gallatin sc->legacy_irq ? "INTx" : "MSI", 47121e413cf9SAndrew Gallatin rman_get_start(sc->irq_res)); 47131e413cf9SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 47141e413cf9SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 471537d89b0cSAndrew Gallatin #if __FreeBSD_version > 700030 471637d89b0cSAndrew Gallatin NULL, 471737d89b0cSAndrew Gallatin #endif 471837d89b0cSAndrew Gallatin mxge_intr, &sc->ss[0], &sc->ih); 47191e413cf9SAndrew Gallatin if (err != 0) { 47201e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, 472191ed8913SAndrew Gallatin sc->legacy_irq ? 0 : 1, sc->irq_res); 472291ed8913SAndrew Gallatin if (!sc->legacy_irq) 47231e413cf9SAndrew Gallatin pci_release_msi(sc->dev); 47241e413cf9SAndrew Gallatin } 47251e413cf9SAndrew Gallatin return err; 47261e413cf9SAndrew Gallatin } 47271e413cf9SAndrew Gallatin 47281e413cf9SAndrew Gallatin static void 47291e413cf9SAndrew Gallatin mxge_rem_msix_irqs(mxge_softc_t *sc) 47301e413cf9SAndrew Gallatin { 47311e413cf9SAndrew Gallatin int i, rid; 47321e413cf9SAndrew Gallatin 47331e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 47341e413cf9SAndrew Gallatin if (sc->msix_ih[i] != NULL) { 47351e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->msix_irq_res[i], 47361e413cf9SAndrew Gallatin sc->msix_ih[i]); 47371e413cf9SAndrew Gallatin sc->msix_ih[i] = NULL; 47381e413cf9SAndrew Gallatin } 47391e413cf9SAndrew Gallatin } 47401e413cf9SAndrew Gallatin free(sc->msix_ih, M_DEVBUF); 47411e413cf9SAndrew Gallatin 47421e413cf9SAndrew Gallatin for (i = 0; i < sc->num_slices; i++) { 47431e413cf9SAndrew Gallatin rid = i + 1; 47441e413cf9SAndrew Gallatin if (sc->msix_irq_res[i] != NULL) 47451e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, rid, 47461e413cf9SAndrew Gallatin sc->msix_irq_res[i]); 47471e413cf9SAndrew Gallatin sc->msix_irq_res[i] = NULL; 47481e413cf9SAndrew Gallatin } 47491e413cf9SAndrew Gallatin free(sc->msix_irq_res, M_DEVBUF); 47501e413cf9SAndrew Gallatin 47511e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2), 47521e413cf9SAndrew Gallatin sc->msix_table_res); 47531e413cf9SAndrew Gallatin 47541e413cf9SAndrew Gallatin pci_release_msi(sc->dev); 47551e413cf9SAndrew Gallatin return; 47561e413cf9SAndrew Gallatin } 47571e413cf9SAndrew Gallatin 47581e413cf9SAndrew Gallatin static void 47591e413cf9SAndrew Gallatin mxge_rem_single_irq(mxge_softc_t *sc) 47601e413cf9SAndrew Gallatin { 47611e413cf9SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 47621e413cf9SAndrew Gallatin bus_release_resource(sc->dev, SYS_RES_IRQ, 476391ed8913SAndrew Gallatin sc->legacy_irq ? 0 : 1, sc->irq_res); 476491ed8913SAndrew Gallatin if (!sc->legacy_irq) 47651e413cf9SAndrew Gallatin pci_release_msi(sc->dev); 47661e413cf9SAndrew Gallatin } 47671e413cf9SAndrew Gallatin 47681e413cf9SAndrew Gallatin static void 47691e413cf9SAndrew Gallatin mxge_rem_irq(mxge_softc_t *sc) 47701e413cf9SAndrew Gallatin { 47711e413cf9SAndrew Gallatin if (sc->num_slices > 1) 47721e413cf9SAndrew Gallatin mxge_rem_msix_irqs(sc); 47731e413cf9SAndrew Gallatin else 47741e413cf9SAndrew Gallatin mxge_rem_single_irq(sc); 47751e413cf9SAndrew Gallatin } 47761e413cf9SAndrew Gallatin 47771e413cf9SAndrew Gallatin static int 47781e413cf9SAndrew Gallatin mxge_add_irq(mxge_softc_t *sc) 47791e413cf9SAndrew Gallatin { 47801e413cf9SAndrew Gallatin int err; 47811e413cf9SAndrew Gallatin 47821e413cf9SAndrew Gallatin if (sc->num_slices > 1) 47831e413cf9SAndrew Gallatin err = mxge_add_msix_irqs(sc); 47841e413cf9SAndrew Gallatin else 47851e413cf9SAndrew Gallatin err = mxge_add_single_irq(sc); 47861e413cf9SAndrew Gallatin 47871e413cf9SAndrew Gallatin if (0 && err == 0 && sc->num_slices > 1) { 47881e413cf9SAndrew Gallatin mxge_rem_msix_irqs(sc); 47891e413cf9SAndrew Gallatin err = mxge_add_msix_irqs(sc); 47901e413cf9SAndrew Gallatin } 47911e413cf9SAndrew Gallatin return err; 47921e413cf9SAndrew Gallatin } 47931e413cf9SAndrew Gallatin 4794b2fc195eSAndrew Gallatin 4795b2fc195eSAndrew Gallatin static int 47966d87a65dSAndrew Gallatin mxge_attach(device_t dev) 4797b2fc195eSAndrew Gallatin { 47980a7a780eSAndrew Gallatin mxge_cmd_t cmd; 47996d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 4800b2fc195eSAndrew Gallatin struct ifnet *ifp; 48011e413cf9SAndrew Gallatin int err, rid; 4802b2fc195eSAndrew Gallatin 4803b2fc195eSAndrew Gallatin sc->dev = dev; 48046d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 4805b2fc195eSAndrew Gallatin 480672c042dfSAndrew Gallatin TASK_INIT(&sc->watchdog_task, 1, mxge_watchdog_task, sc); 4807a4a75d67SJohn Baldwin sc->tq = taskqueue_create("mxge_taskq", M_WAITOK, 4808a4a75d67SJohn Baldwin taskqueue_thread_enqueue, &sc->tq); 480972c042dfSAndrew Gallatin if (sc->tq == NULL) { 481072c042dfSAndrew Gallatin err = ENOMEM; 481172c042dfSAndrew Gallatin goto abort_with_nothing; 481272c042dfSAndrew Gallatin } 481372c042dfSAndrew Gallatin 481462ce43ccSScott Long err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 4815b2fc195eSAndrew Gallatin 1, /* alignment */ 48161e413cf9SAndrew Gallatin 0, /* boundary */ 4817b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 4818b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 4819b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 4820aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 48215e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 48221e413cf9SAndrew Gallatin 65536, /* maxsegsize */ 4823b2fc195eSAndrew Gallatin 0, /* flags */ 4824b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 4825b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 4826b2fc195eSAndrew Gallatin 4827b2fc195eSAndrew Gallatin if (err != 0) { 4828b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 4829b2fc195eSAndrew Gallatin err); 483072c042dfSAndrew Gallatin goto abort_with_tq; 4831b2fc195eSAndrew Gallatin } 4832b2fc195eSAndrew Gallatin 4833b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 4834b2fc195eSAndrew Gallatin if (ifp == NULL) { 4835b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 4836b2fc195eSAndrew Gallatin err = ENOSPC; 4837b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 4838b2fc195eSAndrew Gallatin } 48391e413cf9SAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 48401e413cf9SAndrew Gallatin 4841a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 4842a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 4843a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 4844a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 4845a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 4846a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 4847b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 4848b2fc195eSAndrew Gallatin 4849dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 4850d91b1b49SAndrew Gallatin 4851dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 4852b2fc195eSAndrew Gallatin 4853b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 4854b2fc195eSAndrew Gallatin rid = PCIR_BARS; 485543cd6160SJustin Hibbits sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 485643cd6160SJustin Hibbits RF_ACTIVE); 4857b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 4858b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 4859b2fc195eSAndrew Gallatin err = ENXIO; 4860b2fc195eSAndrew Gallatin goto abort_with_lock; 4861b2fc195eSAndrew Gallatin } 4862b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 4863b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 4864b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 4865da1b038aSJustin Hibbits device_printf(dev, "impossible memory region size %jd\n", 4866b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 4867b2fc195eSAndrew Gallatin err = ENXIO; 4868b2fc195eSAndrew Gallatin goto abort_with_mem_res; 4869b2fc195eSAndrew Gallatin } 4870b2fc195eSAndrew Gallatin 4871b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 4872b2fc195eSAndrew Gallatin lanai SRAM */ 48736d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 4874b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 4875b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 48766d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 4877b2fc195eSAndrew Gallatin sc->eeprom_strings, 48786d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 48796d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 4880b2fc195eSAndrew Gallatin if (err != 0) 4881b2fc195eSAndrew Gallatin goto abort_with_mem_res; 4882b2fc195eSAndrew Gallatin 4883b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 48846d87a65dSAndrew Gallatin mxge_enable_wc(sc); 4885b2fc195eSAndrew Gallatin 4886b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 48876d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 48886d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 4889b2fc195eSAndrew Gallatin if (err != 0) 4890b2fc195eSAndrew Gallatin goto abort_with_mem_res; 4891b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 48926d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 4893b2fc195eSAndrew Gallatin if (err != 0) 4894b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 4895b2fc195eSAndrew Gallatin 4896a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 4897a98d6cd7SAndrew Gallatin if (err != 0) 48981e413cf9SAndrew Gallatin goto abort_with_zeropad_dma; 4899b2fc195eSAndrew Gallatin 49008fe615baSAndrew Gallatin /* select & load the firmware */ 49018fe615baSAndrew Gallatin err = mxge_select_firmware(sc); 4902b2fc195eSAndrew Gallatin if (err != 0) 49031e413cf9SAndrew Gallatin goto abort_with_dmabench; 49045e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 49051e413cf9SAndrew Gallatin 49061e413cf9SAndrew Gallatin mxge_slice_probe(sc); 49071e413cf9SAndrew Gallatin err = mxge_alloc_slices(sc); 49081e413cf9SAndrew Gallatin if (err != 0) 49091e413cf9SAndrew Gallatin goto abort_with_dmabench; 49101e413cf9SAndrew Gallatin 4911adae7080SAndrew Gallatin err = mxge_reset(sc, 0); 4912b2fc195eSAndrew Gallatin if (err != 0) 49131e413cf9SAndrew Gallatin goto abort_with_slices; 4914b2fc195eSAndrew Gallatin 4915a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 4916a98d6cd7SAndrew Gallatin if (err != 0) { 4917a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 49182e084798SAndrew Gallatin goto abort_with_slices; 4919a98d6cd7SAndrew Gallatin } 4920a98d6cd7SAndrew Gallatin 49211e413cf9SAndrew Gallatin err = mxge_add_irq(sc); 4922a98d6cd7SAndrew Gallatin if (err != 0) { 49231e413cf9SAndrew Gallatin device_printf(sc->dev, "failed to add irq\n"); 4924a98d6cd7SAndrew Gallatin goto abort_with_rings; 4925a98d6cd7SAndrew Gallatin } 49261e413cf9SAndrew Gallatin 4927b245f96cSGleb Smirnoff ifp->if_baudrate = IF_Gbps(10); 4928c792928fSAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | 492926dd49c6SAndrew Gallatin IFCAP_VLAN_MTU | IFCAP_LINKSTATE | IFCAP_TXCSUM_IPV6 | 493026dd49c6SAndrew Gallatin IFCAP_RXCSUM_IPV6; 493126dd49c6SAndrew Gallatin #if defined(INET) || defined(INET6) 4932eb6219e3SAndrew Gallatin ifp->if_capabilities |= IFCAP_LRO; 4933eb6219e3SAndrew Gallatin #endif 493437d89b0cSAndrew Gallatin 493537d89b0cSAndrew Gallatin #ifdef MXGE_NEW_VLAN_API 493637d89b0cSAndrew Gallatin ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; 49370dce6781SAndrew Gallatin 49380dce6781SAndrew Gallatin /* Only FW 1.4.32 and newer can do TSO over vlans */ 49390dce6781SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 49400dce6781SAndrew Gallatin sc->fw_ver_tiny >= 32) 49410dce6781SAndrew Gallatin ifp->if_capabilities |= IFCAP_VLAN_HWTSO; 494237d89b0cSAndrew Gallatin #endif 4943053e637fSAndrew Gallatin sc->max_mtu = mxge_max_mtu(sc); 4944053e637fSAndrew Gallatin if (sc->max_mtu >= 9000) 4945053e637fSAndrew Gallatin ifp->if_capabilities |= IFCAP_JUMBO_MTU; 4946053e637fSAndrew Gallatin else 4947053e637fSAndrew Gallatin device_printf(dev, "MTU limited to %d. Install " 4948adae7080SAndrew Gallatin "latest firmware for 9000 byte jumbo support\n", 4949053e637fSAndrew Gallatin sc->max_mtu - ETHER_HDR_LEN); 4950aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 49510a7a780eSAndrew Gallatin ifp->if_hwassist |= CSUM_TCP_IPV6 | CSUM_UDP_IPV6; 49520a7a780eSAndrew Gallatin /* check to see if f/w supports TSO for IPv6 */ 49530a7a780eSAndrew Gallatin if (!mxge_send_cmd(sc, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, &cmd)) { 49540a7a780eSAndrew Gallatin if (CSUM_TCP_IPV6) 49550a7a780eSAndrew Gallatin ifp->if_capabilities |= IFCAP_TSO6; 49560a7a780eSAndrew Gallatin sc->max_tso6_hlen = min(cmd.data0, 49570a7a780eSAndrew Gallatin sizeof (sc->ss[0].scratch)); 49580a7a780eSAndrew Gallatin } 4959b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 4960f04b33f8SAndrew Gallatin if (sc->lro_cnt == 0) 4961f04b33f8SAndrew Gallatin ifp->if_capenable &= ~IFCAP_LRO; 49626d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 4963b2fc195eSAndrew Gallatin ifp->if_softc = sc; 4964b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 49656d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 49666d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 4967f3f040d9SGleb Smirnoff ifp->if_get_counter = mxge_get_counter; 49683d07f894SAndrew Gallatin ifp->if_hw_tsomax = IP_MAXPACKET - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); 49697eafc4d5SAndrew Gallatin ifp->if_hw_tsomaxsegcount = sc->ss[0].tx.max_desc; 49703d07f894SAndrew Gallatin ifp->if_hw_tsomaxsegsize = IP_MAXPACKET; 4971c587e59fSAndrew Gallatin /* Initialise the ifmedia structure */ 4972c587e59fSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 4973c587e59fSAndrew Gallatin mxge_media_status); 4974c406ad2eSAndrew Gallatin mxge_media_init(sc); 4975c587e59fSAndrew Gallatin mxge_media_probe(sc); 49768c5d766cSAndrew Gallatin sc->dying = 0; 4977b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 4978f9453025SAndrew Gallatin /* ether_ifattach sets mtu to ETHERMTU */ 4979f9453025SAndrew Gallatin if (mxge_initial_mtu != ETHERMTU) 4980f9453025SAndrew Gallatin mxge_change_mtu(sc, mxge_initial_mtu); 4981b2fc195eSAndrew Gallatin 49826d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 4983c6cb3e3fSAndrew Gallatin #ifdef IFNET_BUF_RING 4984c6cb3e3fSAndrew Gallatin ifp->if_transmit = mxge_transmit; 4985c6cb3e3fSAndrew Gallatin ifp->if_qflush = mxge_qflush; 4986c6cb3e3fSAndrew Gallatin #endif 49872e084798SAndrew Gallatin taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s taskq", 49882e084798SAndrew Gallatin device_get_nameunit(sc->dev)); 49896b484a49SAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 4990b2fc195eSAndrew Gallatin return 0; 4991b2fc195eSAndrew Gallatin 4992a98d6cd7SAndrew Gallatin abort_with_rings: 4993a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 49941e413cf9SAndrew Gallatin abort_with_slices: 49951e413cf9SAndrew Gallatin mxge_free_slices(sc); 4996a98d6cd7SAndrew Gallatin abort_with_dmabench: 4997a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 4998b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 49996d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 5000b2fc195eSAndrew Gallatin abort_with_cmd_dma: 50016d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 5002b2fc195eSAndrew Gallatin abort_with_mem_res: 5003b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 5004b2fc195eSAndrew Gallatin abort_with_lock: 5005b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 5006a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 5007a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 5008b2fc195eSAndrew Gallatin if_free(ifp); 5009b2fc195eSAndrew Gallatin abort_with_parent_dmat: 5010b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 501172c042dfSAndrew Gallatin abort_with_tq: 501272c042dfSAndrew Gallatin if (sc->tq != NULL) { 501372c042dfSAndrew Gallatin taskqueue_drain(sc->tq, &sc->watchdog_task); 501472c042dfSAndrew Gallatin taskqueue_free(sc->tq); 501572c042dfSAndrew Gallatin sc->tq = NULL; 501672c042dfSAndrew Gallatin } 5017b2fc195eSAndrew Gallatin abort_with_nothing: 5018b2fc195eSAndrew Gallatin return err; 5019b2fc195eSAndrew Gallatin } 5020b2fc195eSAndrew Gallatin 5021b2fc195eSAndrew Gallatin static int 50226d87a65dSAndrew Gallatin mxge_detach(device_t dev) 5023b2fc195eSAndrew Gallatin { 50246d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 5025b2fc195eSAndrew Gallatin 502637d89b0cSAndrew Gallatin if (mxge_vlans_active(sc)) { 5027c792928fSAndrew Gallatin device_printf(sc->dev, 5028c792928fSAndrew Gallatin "Detach vlans before removing module\n"); 5029c792928fSAndrew Gallatin return EBUSY; 5030c792928fSAndrew Gallatin } 5031a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 50328c5d766cSAndrew Gallatin sc->dying = 1; 5033b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 5034a393336bSAndrew Gallatin mxge_close(sc, 0); 5035a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 5036b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 503772c042dfSAndrew Gallatin if (sc->tq != NULL) { 503872c042dfSAndrew Gallatin taskqueue_drain(sc->tq, &sc->watchdog_task); 503972c042dfSAndrew Gallatin taskqueue_free(sc->tq); 504072c042dfSAndrew Gallatin sc->tq = NULL; 504172c042dfSAndrew Gallatin } 5042e749ef6bSAndrew Gallatin callout_drain(&sc->co_hdl); 5043dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 5044091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 50451e413cf9SAndrew Gallatin mxge_rem_sysctls(sc); 50461e413cf9SAndrew Gallatin mxge_rem_irq(sc); 5047a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 50481e413cf9SAndrew Gallatin mxge_free_slices(sc); 5049a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 50506d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 50516d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 5052b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 5053b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 5054a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 5055a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 5056b2fc195eSAndrew Gallatin if_free(sc->ifp); 5057b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 5058b2fc195eSAndrew Gallatin return 0; 5059b2fc195eSAndrew Gallatin } 5060b2fc195eSAndrew Gallatin 5061b2fc195eSAndrew Gallatin static int 50626d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 5063b2fc195eSAndrew Gallatin { 5064b2fc195eSAndrew Gallatin return 0; 5065b2fc195eSAndrew Gallatin } 5066b2fc195eSAndrew Gallatin 5067b2fc195eSAndrew Gallatin /* 5068b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 5069b2fc195eSAndrew Gallatin 5070b2fc195eSAndrew Gallatin Local Variables: 5071b2fc195eSAndrew Gallatin c-file-style:"linux" 5072b2fc195eSAndrew Gallatin tab-width:8 5073b2fc195eSAndrew Gallatin End: 5074b2fc195eSAndrew Gallatin */ 5075