16d87a65dSAndrew Gallatin /****************************************************************************** 2b2fc195eSAndrew Gallatin 3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc. 4b2fc195eSAndrew Gallatin All rights reserved. 5b2fc195eSAndrew Gallatin 6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without 7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met: 8b2fc195eSAndrew Gallatin 9b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice, 10b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer. 11b2fc195eSAndrew Gallatin 12b2fc195eSAndrew Gallatin 2. Redistributions in binary form must reproduce the above copyright 13b2fc195eSAndrew Gallatin notice, this list of conditions and the following disclaimer in the 14b2fc195eSAndrew Gallatin documentation and/or other materials provided with the distribution. 15b2fc195eSAndrew Gallatin 16b2fc195eSAndrew Gallatin 3. Neither the name of the Myricom Inc, nor the names of its 17b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 18b2fc195eSAndrew Gallatin this software without specific prior written permission. 19b2fc195eSAndrew Gallatin 20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 31b2fc195eSAndrew Gallatin 32b2fc195eSAndrew Gallatin ***************************************************************************/ 33b2fc195eSAndrew Gallatin 34b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 36b2fc195eSAndrew Gallatin 37b2fc195eSAndrew Gallatin #include <sys/param.h> 38b2fc195eSAndrew Gallatin #include <sys/systm.h> 39b2fc195eSAndrew Gallatin #include <sys/linker.h> 40b2fc195eSAndrew Gallatin #include <sys/firmware.h> 41b2fc195eSAndrew Gallatin #include <sys/endian.h> 42b2fc195eSAndrew Gallatin #include <sys/sockio.h> 43b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 44b2fc195eSAndrew Gallatin #include <sys/malloc.h> 45b2fc195eSAndrew Gallatin #include <sys/kdb.h> 46b2fc195eSAndrew Gallatin #include <sys/kernel.h> 474e7f640dSJohn Baldwin #include <sys/lock.h> 48b2fc195eSAndrew Gallatin #include <sys/module.h> 49b2fc195eSAndrew Gallatin #include <sys/memrange.h> 50b2fc195eSAndrew Gallatin #include <sys/socket.h> 51b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 52b2fc195eSAndrew Gallatin #include <sys/sx.h> 53b2fc195eSAndrew Gallatin 54b2fc195eSAndrew Gallatin #include <net/if.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 #include <net/zlib.h> 65b2fc195eSAndrew Gallatin 66b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 67b2fc195eSAndrew Gallatin #include <netinet/in.h> 68b2fc195eSAndrew Gallatin #include <netinet/ip.h> 69aed8e389SAndrew Gallatin #include <netinet/tcp.h> 70b2fc195eSAndrew Gallatin 71b2fc195eSAndrew Gallatin #include <machine/bus.h> 72053e637fSAndrew Gallatin #include <machine/in_cksum.h> 73b2fc195eSAndrew Gallatin #include <machine/resource.h> 74b2fc195eSAndrew Gallatin #include <sys/bus.h> 75b2fc195eSAndrew Gallatin #include <sys/rman.h> 76b2fc195eSAndrew Gallatin 77b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 78b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 79b2fc195eSAndrew Gallatin 80b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 81b2fc195eSAndrew Gallatin #include <vm/pmap.h> 82b2fc195eSAndrew Gallatin 836d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 846d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 856d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 86b2fc195eSAndrew Gallatin 87b2fc195eSAndrew Gallatin /* tunable params */ 886d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 89d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0; 906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 926d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 935e7d8541SAndrew Gallatin static int mxge_verbose = 0; 94dce01b9bSAndrew Gallatin static int mxge_ticks; 956d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 966d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 97b2fc195eSAndrew Gallatin 986d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 996d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 1006d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 1016d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1026d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 103b2fc195eSAndrew Gallatin 1046d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 105b2fc195eSAndrew Gallatin { 106b2fc195eSAndrew Gallatin /* Device interface */ 1076d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1086d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1096d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1106d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 111b2fc195eSAndrew Gallatin {0, 0} 112b2fc195eSAndrew Gallatin }; 113b2fc195eSAndrew Gallatin 1146d87a65dSAndrew Gallatin static driver_t mxge_driver = 115b2fc195eSAndrew Gallatin { 1166d87a65dSAndrew Gallatin "mxge", 1176d87a65dSAndrew Gallatin mxge_methods, 1186d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 119b2fc195eSAndrew Gallatin }; 120b2fc195eSAndrew Gallatin 1216d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 122b2fc195eSAndrew Gallatin 123b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1246d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1256d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 126b2fc195eSAndrew Gallatin 1278fe615baSAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc); 1288fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data); 1298fe615baSAndrew Gallatin 130b2fc195eSAndrew Gallatin static int 1316d87a65dSAndrew Gallatin mxge_probe(device_t dev) 132b2fc195eSAndrew Gallatin { 1336d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1346d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 135b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 136b2fc195eSAndrew Gallatin return 0; 137b2fc195eSAndrew Gallatin } 138b2fc195eSAndrew Gallatin return ENXIO; 139b2fc195eSAndrew Gallatin } 140b2fc195eSAndrew Gallatin 141b2fc195eSAndrew Gallatin static void 1426d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 143b2fc195eSAndrew Gallatin { 144b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 145b2fc195eSAndrew Gallatin vm_paddr_t pa; 146b2fc195eSAndrew Gallatin vm_offset_t len; 147b2fc195eSAndrew Gallatin int err, action; 148b2fc195eSAndrew Gallatin 149b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 150b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 151b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 152b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 153b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 154b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1556d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 156b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 157b2fc195eSAndrew Gallatin if (err != 0) { 158b2fc195eSAndrew Gallatin device_printf(sc->dev, 159b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 160b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 161b2fc195eSAndrew Gallatin } else { 162b2fc195eSAndrew Gallatin sc->wc = 1; 163b2fc195eSAndrew Gallatin } 164b2fc195eSAndrew Gallatin } 165b2fc195eSAndrew Gallatin 166b2fc195eSAndrew Gallatin 167b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 168b2fc195eSAndrew Gallatin static void 1696d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 170b2fc195eSAndrew Gallatin int error) 171b2fc195eSAndrew Gallatin { 172b2fc195eSAndrew Gallatin if (error == 0) { 173b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 174b2fc195eSAndrew Gallatin } 175b2fc195eSAndrew Gallatin } 176b2fc195eSAndrew Gallatin 177b2fc195eSAndrew Gallatin static int 1786d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 179b2fc195eSAndrew Gallatin bus_size_t alignment) 180b2fc195eSAndrew Gallatin { 181b2fc195eSAndrew Gallatin int err; 182b2fc195eSAndrew Gallatin device_t dev = sc->dev; 183b2fc195eSAndrew Gallatin 184b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 185b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 186b2fc195eSAndrew Gallatin alignment, /* alignment */ 187b2fc195eSAndrew Gallatin 4096, /* boundary */ 188b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 189b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 190b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 191b2fc195eSAndrew Gallatin bytes, /* maxsize */ 192b2fc195eSAndrew Gallatin 1, /* num segs */ 193b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 194b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 195b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 196b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 197b2fc195eSAndrew Gallatin if (err != 0) { 198b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 199b2fc195eSAndrew Gallatin return err; 200b2fc195eSAndrew Gallatin } 201b2fc195eSAndrew Gallatin 202b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 203b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 204b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 205b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 206b2fc195eSAndrew Gallatin if (err != 0) { 207b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 208b2fc195eSAndrew Gallatin goto abort_with_dmat; 209b2fc195eSAndrew Gallatin } 210b2fc195eSAndrew Gallatin 211b2fc195eSAndrew Gallatin /* load the memory */ 212b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2136d87a65dSAndrew Gallatin mxge_dmamap_callback, 214b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 215b2fc195eSAndrew Gallatin if (err != 0) { 216b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 217b2fc195eSAndrew Gallatin goto abort_with_mem; 218b2fc195eSAndrew Gallatin } 219b2fc195eSAndrew Gallatin return 0; 220b2fc195eSAndrew Gallatin 221b2fc195eSAndrew Gallatin abort_with_mem: 222b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 223b2fc195eSAndrew Gallatin abort_with_dmat: 224b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 225b2fc195eSAndrew Gallatin return err; 226b2fc195eSAndrew Gallatin } 227b2fc195eSAndrew Gallatin 228b2fc195eSAndrew Gallatin 229b2fc195eSAndrew Gallatin static void 2306d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 231b2fc195eSAndrew Gallatin { 232b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 233b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 234b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 235b2fc195eSAndrew Gallatin } 236b2fc195eSAndrew Gallatin 237b2fc195eSAndrew Gallatin /* 238b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 239b2fc195eSAndrew Gallatin * SN=x\0 240b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 241b2fc195eSAndrew Gallatin * PC=text\0 242b2fc195eSAndrew Gallatin */ 243b2fc195eSAndrew Gallatin 244b2fc195eSAndrew Gallatin static int 2456d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 246b2fc195eSAndrew Gallatin { 2476d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 248b2fc195eSAndrew Gallatin 249b2fc195eSAndrew Gallatin char *ptr, *limit; 250b2fc195eSAndrew Gallatin int i, found_mac; 251b2fc195eSAndrew Gallatin 252b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2536d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 254b2fc195eSAndrew Gallatin found_mac = 0; 255b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 256b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2575e7d8541SAndrew Gallatin ptr += 1; 258b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 259b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2605e7d8541SAndrew Gallatin ptr += 3; 261b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 262b2fc195eSAndrew Gallatin goto abort; 263b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 264b2fc195eSAndrew Gallatin found_mac = 1; 265b2fc195eSAndrew Gallatin } 2665e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2675e7d8541SAndrew Gallatin ptr += 3; 2685e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2695e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2705e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2715e7d8541SAndrew Gallatin ptr += 3; 2725e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2735e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 274b2fc195eSAndrew Gallatin } 2756d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 276b2fc195eSAndrew Gallatin } 277b2fc195eSAndrew Gallatin 278b2fc195eSAndrew Gallatin if (found_mac) 279b2fc195eSAndrew Gallatin return 0; 280b2fc195eSAndrew Gallatin 281b2fc195eSAndrew Gallatin abort: 282b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 283b2fc195eSAndrew Gallatin 284b2fc195eSAndrew Gallatin return ENXIO; 285b2fc195eSAndrew Gallatin } 286b2fc195eSAndrew Gallatin 287b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 2888fe615baSAndrew Gallatin static void 2898fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc) 290b2fc195eSAndrew Gallatin { 291b2fc195eSAndrew Gallatin uint32_t val; 2928fe615baSAndrew Gallatin unsigned long base, off; 293b2fc195eSAndrew Gallatin char *va, *cfgptr; 2948fe615baSAndrew Gallatin device_t pdev, mcp55; 2958fe615baSAndrew Gallatin uint16_t vendor_id, device_id, word; 296b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 297b2fc195eSAndrew Gallatin uint32_t *ptr32; 298b2fc195eSAndrew Gallatin 2998fe615baSAndrew Gallatin 3008fe615baSAndrew Gallatin if (!mxge_nvidia_ecrc_enable) 3018fe615baSAndrew Gallatin return; 3028fe615baSAndrew Gallatin 3038fe615baSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 3048fe615baSAndrew Gallatin if (pdev == NULL) { 3058fe615baSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 3068fe615baSAndrew Gallatin return; 3078fe615baSAndrew Gallatin } 3088fe615baSAndrew Gallatin vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2); 3098fe615baSAndrew Gallatin device_id = pci_read_config(pdev, PCIR_DEVICE, 2); 3108fe615baSAndrew Gallatin 3118fe615baSAndrew Gallatin if (vendor_id != 0x10de) 3128fe615baSAndrew Gallatin return; 3138fe615baSAndrew Gallatin 3148fe615baSAndrew Gallatin base = 0; 3158fe615baSAndrew Gallatin 3168fe615baSAndrew Gallatin if (device_id == 0x005d) { 3178fe615baSAndrew Gallatin /* ck804, base address is magic */ 3188fe615baSAndrew Gallatin base = 0xe0000000UL; 3198fe615baSAndrew Gallatin } else if (device_id >= 0x0374 && device_id <= 0x378) { 3208fe615baSAndrew Gallatin /* mcp55, base address stored in chipset */ 3218fe615baSAndrew Gallatin mcp55 = pci_find_bsf(0, 0, 0); 3228fe615baSAndrew Gallatin if (mcp55 && 3238fe615baSAndrew Gallatin 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) && 3248fe615baSAndrew Gallatin 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) { 3258fe615baSAndrew Gallatin word = pci_read_config(mcp55, 0x90, 2); 3268fe615baSAndrew Gallatin base = ((unsigned long)word & 0x7ffeU) << 25; 3278fe615baSAndrew Gallatin } 3288fe615baSAndrew Gallatin } 3298fe615baSAndrew Gallatin if (!base) 3308fe615baSAndrew Gallatin return; 3318fe615baSAndrew Gallatin 332b2fc195eSAndrew Gallatin /* XXXX 333b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 334b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 335b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 336b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 337b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 338b2fc195eSAndrew Gallatin */ 339b2fc195eSAndrew Gallatin #if 0 340b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 341b2fc195eSAndrew Gallatin config space */ 342b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 343b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 344b2fc195eSAndrew Gallatin val |= 0x40; 345b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 3468fe615baSAndrew Gallatin return; 347b2fc195eSAndrew Gallatin } 348b2fc195eSAndrew Gallatin #endif 349b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 350b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 351b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 352b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 353b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 354b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 355b2fc195eSAndrew Gallatin */ 356b2fc195eSAndrew Gallatin 357b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 358b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 359b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 360b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 361b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 362b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 363b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 364b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 365b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 366b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 367b2fc195eSAndrew Gallatin 3688fe615baSAndrew Gallatin off = base 369b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 370b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 371b2fc195eSAndrew Gallatin + 8 * slot); 372b2fc195eSAndrew Gallatin 373b2fc195eSAndrew Gallatin /* map it into the kernel */ 374b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 375b2fc195eSAndrew Gallatin 376b2fc195eSAndrew Gallatin 377b2fc195eSAndrew Gallatin if (va == NULL) { 378b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 3798fe615baSAndrew Gallatin return; 380b2fc195eSAndrew Gallatin } 381b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 382b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 383b2fc195eSAndrew Gallatin 384b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 385b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 386b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 387b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 388b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 389b2fc195eSAndrew Gallatin vendor_id, device_id); 390b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3918fe615baSAndrew Gallatin return; 392b2fc195eSAndrew Gallatin } 393b2fc195eSAndrew Gallatin 394b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 395b2fc195eSAndrew Gallatin val = *ptr32; 396b2fc195eSAndrew Gallatin 397b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 398b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 399b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4008fe615baSAndrew Gallatin return; 401b2fc195eSAndrew Gallatin } 402b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 403b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4045e7d8541SAndrew Gallatin if (mxge_verbose) 405b2fc195eSAndrew Gallatin device_printf(sc->dev, 4065e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 4075e7d8541SAndrew Gallatin "at %d:%d:%d\n", 408b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 4098fe615baSAndrew Gallatin return; 410b2fc195eSAndrew Gallatin } 411b2fc195eSAndrew Gallatin #else 4128fe615baSAndrew Gallatin static void 4136d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 414b2fc195eSAndrew Gallatin { 415b2fc195eSAndrew Gallatin device_printf(sc->dev, 416b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 4178fe615baSAndrew Gallatin return; 418b2fc195eSAndrew Gallatin } 419b2fc195eSAndrew Gallatin #endif 4208fe615baSAndrew Gallatin 4218fe615baSAndrew Gallatin 4228fe615baSAndrew Gallatin static int 4238fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type) 4248fe615baSAndrew Gallatin { 4258fe615baSAndrew Gallatin mxge_cmd_t cmd; 4268fe615baSAndrew Gallatin bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr; 4278fe615baSAndrew Gallatin int status; 4288fe615baSAndrew Gallatin uint32_t len; 4298fe615baSAndrew Gallatin char *test = " "; 4308fe615baSAndrew Gallatin 4318fe615baSAndrew Gallatin 4328fe615baSAndrew Gallatin /* Run a small DMA test. 4338fe615baSAndrew Gallatin * The magic multipliers to the length tell the firmware 4348fe615baSAndrew Gallatin * to do DMA read, write, or read+write tests. The 4358fe615baSAndrew Gallatin * results are returned in cmd.data0. The upper 16 4368fe615baSAndrew Gallatin * bits of the return is the number of transfers completed. 4378fe615baSAndrew Gallatin * The lower 16 bits is the time in 0.5us ticks that the 4388fe615baSAndrew Gallatin * transfers took to complete. 4398fe615baSAndrew Gallatin */ 4408fe615baSAndrew Gallatin 4418fe615baSAndrew Gallatin len = sc->tx.boundary; 4428fe615baSAndrew Gallatin 4438fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4448fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4458fe615baSAndrew Gallatin cmd.data2 = len * 0x10000; 4468fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4478fe615baSAndrew Gallatin if (status != 0) { 4488fe615baSAndrew Gallatin test = "read"; 4498fe615baSAndrew Gallatin goto abort; 4508fe615baSAndrew Gallatin } 4518fe615baSAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * len * 2) / 4528fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4538fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4548fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4558fe615baSAndrew Gallatin cmd.data2 = len * 0x1; 4568fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4578fe615baSAndrew Gallatin if (status != 0) { 4588fe615baSAndrew Gallatin test = "write"; 4598fe615baSAndrew Gallatin goto abort; 4608fe615baSAndrew Gallatin } 4618fe615baSAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * len * 2) / 4628fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4638fe615baSAndrew Gallatin 4648fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4658fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4668fe615baSAndrew Gallatin cmd.data2 = len * 0x10001; 4678fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4688fe615baSAndrew Gallatin if (status != 0) { 4698fe615baSAndrew Gallatin test = "read/write"; 4708fe615baSAndrew Gallatin goto abort; 4718fe615baSAndrew Gallatin } 4728fe615baSAndrew Gallatin sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) / 4738fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4748fe615baSAndrew Gallatin 4758fe615baSAndrew Gallatin abort: 4768fe615baSAndrew Gallatin if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) 4778fe615baSAndrew Gallatin device_printf(sc->dev, "DMA %s benchmark failed: %d\n", 4788fe615baSAndrew Gallatin test, status); 4798fe615baSAndrew Gallatin 4808fe615baSAndrew Gallatin return status; 4818fe615baSAndrew Gallatin } 4828fe615baSAndrew Gallatin 483b2fc195eSAndrew Gallatin /* 484b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 485b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 486b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 487b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 488b2fc195eSAndrew Gallatin * ECRC generation (if supported). 489b2fc195eSAndrew Gallatin * 490b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 491b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 492b2fc195eSAndrew Gallatin * 493b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 494b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 495b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 496b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 497b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 498b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 499b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 500b2fc195eSAndrew Gallatin */ 501b2fc195eSAndrew Gallatin 5028fe615baSAndrew Gallatin static int 5038fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc) 5048fe615baSAndrew Gallatin { 5058fe615baSAndrew Gallatin device_t dev = sc->dev; 5068fe615baSAndrew Gallatin int reg, status; 5078fe615baSAndrew Gallatin uint16_t pectl; 5088fe615baSAndrew Gallatin 5098fe615baSAndrew Gallatin sc->tx.boundary = 4096; 5108fe615baSAndrew Gallatin /* 5118fe615baSAndrew Gallatin * Verify the max read request size was set to 4KB 5128fe615baSAndrew Gallatin * before trying the test with 4KB. 5138fe615baSAndrew Gallatin */ 5148fe615baSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 5158fe615baSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 5168fe615baSAndrew Gallatin if ((pectl & (5 << 12)) != (5 << 12)) { 5178fe615baSAndrew Gallatin device_printf(dev, "Max Read Req. size != 4k (0x%x\n", 5188fe615baSAndrew Gallatin pectl); 5198fe615baSAndrew Gallatin sc->tx.boundary = 2048; 5208fe615baSAndrew Gallatin } 5218fe615baSAndrew Gallatin } 5228fe615baSAndrew Gallatin 5238fe615baSAndrew Gallatin /* 5248fe615baSAndrew Gallatin * load the optimized firmware (which assumes aligned PCIe 5258fe615baSAndrew Gallatin * completions) in order to see if it works on this host. 5268fe615baSAndrew Gallatin */ 5278fe615baSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 5288fe615baSAndrew Gallatin status = mxge_load_firmware(sc); 5298fe615baSAndrew Gallatin if (status != 0) { 5308fe615baSAndrew Gallatin return status; 5318fe615baSAndrew Gallatin } 5328fe615baSAndrew Gallatin 5338fe615baSAndrew Gallatin /* 5348fe615baSAndrew Gallatin * Enable ECRC if possible 5358fe615baSAndrew Gallatin */ 5368fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(sc); 5378fe615baSAndrew Gallatin 5388fe615baSAndrew Gallatin /* 5398fe615baSAndrew Gallatin * Run a DMA test which watches for unaligned completions and 5408fe615baSAndrew Gallatin * aborts on the first one seen. 5418fe615baSAndrew Gallatin */ 5428fe615baSAndrew Gallatin 5438fe615baSAndrew Gallatin status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST); 5448fe615baSAndrew Gallatin if (status == 0) 5458fe615baSAndrew Gallatin return 0; /* keep the aligned firmware */ 5468fe615baSAndrew Gallatin 5478fe615baSAndrew Gallatin if (status != E2BIG) 5488fe615baSAndrew Gallatin device_printf(dev, "DMA test failed: %d\n", status); 5498fe615baSAndrew Gallatin if (status == ENOSYS) 5508fe615baSAndrew Gallatin device_printf(dev, "Falling back to ethp! " 5518fe615baSAndrew Gallatin "Please install up to date fw\n"); 5528fe615baSAndrew Gallatin return status; 5538fe615baSAndrew Gallatin } 5548fe615baSAndrew Gallatin 5558fe615baSAndrew Gallatin static int 5566d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 557b2fc195eSAndrew Gallatin { 5588fe615baSAndrew Gallatin int aligned = 0; 559b2fc195eSAndrew Gallatin 560d91b1b49SAndrew Gallatin 561d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 562d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 563d91b1b49SAndrew Gallatin aligned = 1; 564d91b1b49SAndrew Gallatin else 565d91b1b49SAndrew Gallatin aligned = 0; 566d91b1b49SAndrew Gallatin if (mxge_verbose) 567d91b1b49SAndrew Gallatin device_printf(sc->dev, 568d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 569d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 570d91b1b49SAndrew Gallatin goto abort; 571d91b1b49SAndrew Gallatin } 572d91b1b49SAndrew Gallatin 573d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 574d91b1b49SAndrew Gallatin firmware and skip any checks */ 575d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 576d91b1b49SAndrew Gallatin device_printf(sc->dev, 577d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 578d91b1b49SAndrew Gallatin sc->link_width); 579d91b1b49SAndrew Gallatin aligned = 1; 580d91b1b49SAndrew Gallatin goto abort; 581d91b1b49SAndrew Gallatin } 582d91b1b49SAndrew Gallatin 5838fe615baSAndrew Gallatin if (0 == mxge_firmware_probe(sc)) 5848fe615baSAndrew Gallatin return 0; 585b2fc195eSAndrew Gallatin 586b2fc195eSAndrew Gallatin abort: 587b2fc195eSAndrew Gallatin if (aligned) { 5886d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 589b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 590b2fc195eSAndrew Gallatin } else { 5916d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 592b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 593b2fc195eSAndrew Gallatin } 5948fe615baSAndrew Gallatin return (mxge_load_firmware(sc)); 595b2fc195eSAndrew Gallatin } 596b2fc195eSAndrew Gallatin 597b2fc195eSAndrew Gallatin union qualhack 598b2fc195eSAndrew Gallatin { 599b2fc195eSAndrew Gallatin const char *ro_char; 600b2fc195eSAndrew Gallatin char *rw_char; 601b2fc195eSAndrew Gallatin }; 602b2fc195eSAndrew Gallatin 6034da0d523SAndrew Gallatin static int 6044da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 6054da0d523SAndrew Gallatin { 606b824b7d8SAndrew Gallatin 6074da0d523SAndrew Gallatin 6084da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 6094da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 6104da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 6114da0d523SAndrew Gallatin return EIO; 6124da0d523SAndrew Gallatin } 6134da0d523SAndrew Gallatin 6144da0d523SAndrew Gallatin /* save firmware version for sysctl */ 6154da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 6164da0d523SAndrew Gallatin if (mxge_verbose) 6174da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 6184da0d523SAndrew Gallatin 619b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 620b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 6214da0d523SAndrew Gallatin 622b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 623b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 6244da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 6254da0d523SAndrew Gallatin sc->fw_version); 6264da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 6274da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 6284da0d523SAndrew Gallatin return EINVAL; 6294da0d523SAndrew Gallatin } 6304da0d523SAndrew Gallatin return 0; 6314da0d523SAndrew Gallatin 6324da0d523SAndrew Gallatin } 633b2fc195eSAndrew Gallatin 634b2fc195eSAndrew Gallatin static int 6356d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 636b2fc195eSAndrew Gallatin { 63733d54970SLuigi Rizzo const struct firmware *fw; 638b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 639b2fc195eSAndrew Gallatin unsigned hdr_offset; 640b2fc195eSAndrew Gallatin const char *fw_data; 641b2fc195eSAndrew Gallatin union qualhack hack; 642b2fc195eSAndrew Gallatin int status; 6434da0d523SAndrew Gallatin unsigned int i; 6444da0d523SAndrew Gallatin char dummy; 645b2fc195eSAndrew Gallatin 646b2fc195eSAndrew Gallatin 647b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 648b2fc195eSAndrew Gallatin 649b2fc195eSAndrew Gallatin if (fw == NULL) { 650b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 651b2fc195eSAndrew Gallatin sc->fw_name); 652b2fc195eSAndrew Gallatin return ENOENT; 653b2fc195eSAndrew Gallatin } 654b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 655b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 656b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 657b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 658b2fc195eSAndrew Gallatin status = ENOSPC; 659b2fc195eSAndrew Gallatin goto abort_with_fw; 660b2fc195eSAndrew Gallatin } 661b2fc195eSAndrew Gallatin *limit = fw->datasize; 662b2fc195eSAndrew Gallatin 663b2fc195eSAndrew Gallatin /* check id */ 664b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 665b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 666b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 667b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 668b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 669b2fc195eSAndrew Gallatin status = EIO; 670b2fc195eSAndrew Gallatin goto abort_with_fw; 671b2fc195eSAndrew Gallatin } 672b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 673b2fc195eSAndrew Gallatin 6744da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 6754da0d523SAndrew Gallatin if (status != 0) 6764da0d523SAndrew Gallatin goto abort_with_fw; 677b2fc195eSAndrew Gallatin 678b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 679b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 6804da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 6814da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 6824da0d523SAndrew Gallatin hack.rw_char + i, 6834da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 6844da0d523SAndrew Gallatin mb(); 6854da0d523SAndrew Gallatin dummy = *sc->sram; 6864da0d523SAndrew Gallatin mb(); 6874da0d523SAndrew Gallatin } 688b2fc195eSAndrew Gallatin 689b2fc195eSAndrew Gallatin status = 0; 690b2fc195eSAndrew Gallatin abort_with_fw: 691b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 692b2fc195eSAndrew Gallatin return status; 693b2fc195eSAndrew Gallatin } 694b2fc195eSAndrew Gallatin 695b2fc195eSAndrew Gallatin /* 696b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 697b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 698b2fc195eSAndrew Gallatin */ 699b2fc195eSAndrew Gallatin 700b2fc195eSAndrew Gallatin static void 7016d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 702b2fc195eSAndrew Gallatin { 703b2fc195eSAndrew Gallatin char buf_bytes[72]; 704b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 705b2fc195eSAndrew Gallatin volatile char *submit; 706b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 707b2fc195eSAndrew Gallatin int i; 708b2fc195eSAndrew Gallatin 709b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 710b2fc195eSAndrew Gallatin 711b2fc195eSAndrew Gallatin /* clear confirmation addr */ 712b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 713b2fc195eSAndrew Gallatin *confirm = 0; 714b2fc195eSAndrew Gallatin mb(); 715b2fc195eSAndrew Gallatin 716b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 717b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 718b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 719b2fc195eSAndrew Gallatin */ 720b2fc195eSAndrew Gallatin 7216d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7226d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 723b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 724b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 725b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 7266d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 7276d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 728b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 729b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 730b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 731b2fc195eSAndrew Gallatin 732b2fc195eSAndrew Gallatin 7330fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 734b2fc195eSAndrew Gallatin 7356d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 736b2fc195eSAndrew Gallatin mb(); 737b2fc195eSAndrew Gallatin DELAY(1000); 738b2fc195eSAndrew Gallatin mb(); 739b2fc195eSAndrew Gallatin i = 0; 740b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 741b2fc195eSAndrew Gallatin DELAY(1000); 742b2fc195eSAndrew Gallatin i++; 743b2fc195eSAndrew Gallatin } 744b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 745b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 746b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 747b2fc195eSAndrew Gallatin *confirm); 748b2fc195eSAndrew Gallatin } 749b2fc195eSAndrew Gallatin return; 750b2fc195eSAndrew Gallatin } 751b2fc195eSAndrew Gallatin 752b2fc195eSAndrew Gallatin static int 7536d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 754b2fc195eSAndrew Gallatin { 755b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 756b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 757b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 7580fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 759b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 760e0501fd0SAndrew Gallatin int err, sleep_total = 0; 761b2fc195eSAndrew Gallatin 762b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 763b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 764b2fc195eSAndrew Gallatin 765b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 766b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 767b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 768b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 7696d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7706d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 771b2fc195eSAndrew Gallatin 772b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 773b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 774a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 775b2fc195eSAndrew Gallatin response->result = 0xffffffff; 776b2fc195eSAndrew Gallatin mb(); 7776d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 778b2fc195eSAndrew Gallatin 7795e7d8541SAndrew Gallatin /* wait up to 20ms */ 780e0501fd0SAndrew Gallatin err = EAGAIN; 7815e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 782b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 783b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 784b2fc195eSAndrew Gallatin mb(); 785e0501fd0SAndrew Gallatin switch (be32toh(response->result)) { 786e0501fd0SAndrew Gallatin case 0: 787b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 788e0501fd0SAndrew Gallatin err = 0; 789e0501fd0SAndrew Gallatin break; 790e0501fd0SAndrew Gallatin case 0xffffffff: 791e0501fd0SAndrew Gallatin DELAY(1000); 792e0501fd0SAndrew Gallatin break; 793e0501fd0SAndrew Gallatin case MXGEFW_CMD_UNKNOWN: 794e0501fd0SAndrew Gallatin err = ENOSYS; 795e0501fd0SAndrew Gallatin break; 796e0501fd0SAndrew Gallatin case MXGEFW_CMD_ERROR_UNALIGNED: 797e0501fd0SAndrew Gallatin err = E2BIG; 798e0501fd0SAndrew Gallatin break; 799e0501fd0SAndrew Gallatin default: 800b2fc195eSAndrew Gallatin device_printf(sc->dev, 8016d87a65dSAndrew Gallatin "mxge: command %d " 802b2fc195eSAndrew Gallatin "failed, result = %d\n", 803b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 804e0501fd0SAndrew Gallatin err = ENXIO; 805e0501fd0SAndrew Gallatin break; 806b2fc195eSAndrew Gallatin } 807e0501fd0SAndrew Gallatin if (err != EAGAIN) 808e0501fd0SAndrew Gallatin break; 809b2fc195eSAndrew Gallatin } 810e0501fd0SAndrew Gallatin if (err == EAGAIN) 8116d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 812b2fc195eSAndrew Gallatin "result = %d\n", 813b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 814e0501fd0SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 815e0501fd0SAndrew Gallatin return err; 816b2fc195eSAndrew Gallatin } 817b2fc195eSAndrew Gallatin 8184da0d523SAndrew Gallatin static int 8194da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 8204da0d523SAndrew Gallatin { 8214da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 8224da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 8234da0d523SAndrew Gallatin size_t hdr_offset; 8244da0d523SAndrew Gallatin int status; 8254da0d523SAndrew Gallatin 8264da0d523SAndrew Gallatin /* find running firmware header */ 8274da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 8284da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 8294da0d523SAndrew Gallatin 8304da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 8314da0d523SAndrew Gallatin device_printf(sc->dev, 8324da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 8334da0d523SAndrew Gallatin (int)hdr_offset); 8344da0d523SAndrew Gallatin return EIO; 8354da0d523SAndrew Gallatin } 8364da0d523SAndrew Gallatin 8374da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 8384da0d523SAndrew Gallatin * validate firmware */ 8394da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 8404da0d523SAndrew Gallatin if (hdr == NULL) { 8414da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 8424da0d523SAndrew Gallatin return ENOMEM; 8434da0d523SAndrew Gallatin } 8444da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 8454da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 8464da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 8474da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 8484da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 849b824b7d8SAndrew Gallatin 850b824b7d8SAndrew Gallatin /* 851b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 852b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 853b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 854b824b7d8SAndrew Gallatin */ 855b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 856b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 857b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 858b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 859b824b7d8SAndrew Gallatin "working around rx filter bug\n", 860b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 861b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 862b824b7d8SAndrew Gallatin } 863b824b7d8SAndrew Gallatin 8644da0d523SAndrew Gallatin return status; 8654da0d523SAndrew Gallatin } 8664da0d523SAndrew Gallatin 867b2fc195eSAndrew Gallatin 868b2fc195eSAndrew Gallatin static int 8696d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 870b2fc195eSAndrew Gallatin { 871b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 872b2fc195eSAndrew Gallatin volatile char *submit; 873b2fc195eSAndrew Gallatin char buf_bytes[72]; 874b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 875b2fc195eSAndrew Gallatin int status, i; 876b2fc195eSAndrew Gallatin 877b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 878b2fc195eSAndrew Gallatin 879b2fc195eSAndrew Gallatin size = sc->sram_size; 8806d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 881b2fc195eSAndrew Gallatin if (status) { 8824da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 8834da0d523SAndrew Gallatin it is new enough */ 8844da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 8854da0d523SAndrew Gallatin if (status) { 8864da0d523SAndrew Gallatin device_printf(sc->dev, 8874da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 888b2fc195eSAndrew Gallatin return status; 889b2fc195eSAndrew Gallatin } 8904da0d523SAndrew Gallatin device_printf(sc->dev, 8914da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 8924da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 8934da0d523SAndrew Gallatin device_printf(sc->dev, 8944da0d523SAndrew Gallatin "Using firmware currently running on NIC" 8954da0d523SAndrew Gallatin ". For optimal\n"); 8964da0d523SAndrew Gallatin device_printf(sc->dev, 8974da0d523SAndrew Gallatin "performance consider loading optimized " 8984da0d523SAndrew Gallatin "firmware\n"); 8994da0d523SAndrew Gallatin } 900d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 901d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 902d91b1b49SAndrew Gallatin return 0; 9034da0d523SAndrew Gallatin } 904b2fc195eSAndrew Gallatin /* clear confirmation addr */ 905b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 906b2fc195eSAndrew Gallatin *confirm = 0; 907b2fc195eSAndrew Gallatin mb(); 908b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 909b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 910b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 911b2fc195eSAndrew Gallatin */ 912b2fc195eSAndrew Gallatin 9136d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 9146d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 915b2fc195eSAndrew Gallatin 916b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 917b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 918b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 919b2fc195eSAndrew Gallatin 920b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 921b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 922b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 923b2fc195eSAndrew Gallatin */ 924b2fc195eSAndrew Gallatin /* where the code starts*/ 9256d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 926b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 927b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 928b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 929b2fc195eSAndrew Gallatin 9300fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 9316d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 932b2fc195eSAndrew Gallatin mb(); 933b2fc195eSAndrew Gallatin DELAY(1000); 934b2fc195eSAndrew Gallatin mb(); 935b2fc195eSAndrew Gallatin i = 0; 936b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 937b2fc195eSAndrew Gallatin DELAY(1000*10); 938b2fc195eSAndrew Gallatin i++; 939b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 940b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 941b2fc195eSAndrew Gallatin } 942b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 943b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 944b2fc195eSAndrew Gallatin confirm, *confirm); 945b2fc195eSAndrew Gallatin 946b2fc195eSAndrew Gallatin return ENXIO; 947b2fc195eSAndrew Gallatin } 948b2fc195eSAndrew Gallatin return 0; 949b2fc195eSAndrew Gallatin } 950b2fc195eSAndrew Gallatin 951b2fc195eSAndrew Gallatin static int 9526d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 953b2fc195eSAndrew Gallatin { 9546d87a65dSAndrew Gallatin mxge_cmd_t cmd; 955b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 956b2fc195eSAndrew Gallatin int status; 957b2fc195eSAndrew Gallatin 958b2fc195eSAndrew Gallatin 959b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 960b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 961b2fc195eSAndrew Gallatin 962b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 963b2fc195eSAndrew Gallatin 9645e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 965b2fc195eSAndrew Gallatin return status; 966b2fc195eSAndrew Gallatin } 967b2fc195eSAndrew Gallatin 968b2fc195eSAndrew Gallatin static int 9696d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 970b2fc195eSAndrew Gallatin { 9716d87a65dSAndrew Gallatin mxge_cmd_t cmd; 972b2fc195eSAndrew Gallatin int status; 973b2fc195eSAndrew Gallatin 974b2fc195eSAndrew Gallatin if (pause) 9755e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 976b2fc195eSAndrew Gallatin &cmd); 977b2fc195eSAndrew Gallatin else 9785e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 979b2fc195eSAndrew Gallatin &cmd); 980b2fc195eSAndrew Gallatin 981b2fc195eSAndrew Gallatin if (status) { 982b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 983b2fc195eSAndrew Gallatin return ENXIO; 984b2fc195eSAndrew Gallatin } 985b2fc195eSAndrew Gallatin sc->pause = pause; 986b2fc195eSAndrew Gallatin return 0; 987b2fc195eSAndrew Gallatin } 988b2fc195eSAndrew Gallatin 989b2fc195eSAndrew Gallatin static void 9906d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 991b2fc195eSAndrew Gallatin { 9926d87a65dSAndrew Gallatin mxge_cmd_t cmd; 993b2fc195eSAndrew Gallatin int status; 994b2fc195eSAndrew Gallatin 995b2fc195eSAndrew Gallatin if (promisc) 9965e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 997b2fc195eSAndrew Gallatin &cmd); 998b2fc195eSAndrew Gallatin else 9995e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 1000b2fc195eSAndrew Gallatin &cmd); 1001b2fc195eSAndrew Gallatin 1002b2fc195eSAndrew Gallatin if (status) { 1003b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 1004b2fc195eSAndrew Gallatin } 1005b2fc195eSAndrew Gallatin } 1006b2fc195eSAndrew Gallatin 10070fa7f681SAndrew Gallatin static void 10080fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 10090fa7f681SAndrew Gallatin { 10100fa7f681SAndrew Gallatin mxge_cmd_t cmd; 10110fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 10120fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 10130fa7f681SAndrew Gallatin int err; 10140fa7f681SAndrew Gallatin 10150fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 10160fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 10170fa7f681SAndrew Gallatin return; 10180fa7f681SAndrew Gallatin 10190fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 10200fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 10210fa7f681SAndrew Gallatin if (err != 0) { 10220fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 10230fa7f681SAndrew Gallatin " error status: %d\n", err); 10240fa7f681SAndrew Gallatin return; 10250fa7f681SAndrew Gallatin } 10260fa7f681SAndrew Gallatin 1027b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 1028b824b7d8SAndrew Gallatin return; 10290fa7f681SAndrew Gallatin 10300fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 10310fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 10320fa7f681SAndrew Gallatin return; 10330fa7f681SAndrew Gallatin 10340fa7f681SAndrew Gallatin /* Flush all the filters */ 10350fa7f681SAndrew Gallatin 10360fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 10370fa7f681SAndrew Gallatin if (err != 0) { 10380fa7f681SAndrew Gallatin device_printf(sc->dev, 10390fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 10400fa7f681SAndrew Gallatin ", error status: %d\n", err); 10410fa7f681SAndrew Gallatin return; 10420fa7f681SAndrew Gallatin } 10430fa7f681SAndrew Gallatin 10440fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 10450fa7f681SAndrew Gallatin 10460fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 10470fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 10480fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 10490fa7f681SAndrew Gallatin continue; 10500fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 10510fa7f681SAndrew Gallatin &cmd.data0, 4); 10520fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 10530fa7f681SAndrew Gallatin &cmd.data1, 2); 10540fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 10550fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 10560fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 10570fa7f681SAndrew Gallatin if (err != 0) { 10580fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 10590fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 10600fa7f681SAndrew Gallatin "%d\t", err); 10610fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 10620fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10630fa7f681SAndrew Gallatin return; 10640fa7f681SAndrew Gallatin } 10650fa7f681SAndrew Gallatin } 10660fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10670fa7f681SAndrew Gallatin /* Enable multicast filtering */ 10680fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 10690fa7f681SAndrew Gallatin if (err != 0) { 10700fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 10710fa7f681SAndrew Gallatin ", error status: %d\n", err); 10720fa7f681SAndrew Gallatin } 10730fa7f681SAndrew Gallatin } 10740fa7f681SAndrew Gallatin 1075b2fc195eSAndrew Gallatin static int 1076053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc) 1077053e637fSAndrew Gallatin { 1078053e637fSAndrew Gallatin mxge_cmd_t cmd; 1079053e637fSAndrew Gallatin int status; 1080053e637fSAndrew Gallatin 1081c792928fSAndrew Gallatin if (MJUMPAGESIZE - MXGEFW_PAD > MXGEFW_MAX_MTU) 1082c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1083053e637fSAndrew Gallatin 1084053e637fSAndrew Gallatin /* try to set nbufs to see if it we can 1085053e637fSAndrew Gallatin use virtually contiguous jumbos */ 1086053e637fSAndrew Gallatin cmd.data0 = 0; 1087053e637fSAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 1088053e637fSAndrew Gallatin &cmd); 1089053e637fSAndrew Gallatin if (status == 0) 1090c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1091053e637fSAndrew Gallatin 1092053e637fSAndrew Gallatin /* otherwise, we're limited to MJUMPAGESIZE */ 1093053e637fSAndrew Gallatin return MJUMPAGESIZE - MXGEFW_PAD; 1094053e637fSAndrew Gallatin } 1095053e637fSAndrew Gallatin 1096053e637fSAndrew Gallatin static int 1097adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup) 1098b2fc195eSAndrew Gallatin { 1099b2fc195eSAndrew Gallatin 11006d87a65dSAndrew Gallatin mxge_cmd_t cmd; 11015e7d8541SAndrew Gallatin size_t bytes; 11025e7d8541SAndrew Gallatin int status; 1103b2fc195eSAndrew Gallatin 1104b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 1105b2fc195eSAndrew Gallatin is alive */ 1106b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 11075e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 1108b2fc195eSAndrew Gallatin if (status != 0) { 1109b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 1110b2fc195eSAndrew Gallatin return ENXIO; 1111b2fc195eSAndrew Gallatin } 1112b2fc195eSAndrew Gallatin 1113091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 1114091feecdSAndrew Gallatin 1115adae7080SAndrew Gallatin if (interrupts_setup) { 1116b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 1117adae7080SAndrew Gallatin bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); 11185e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 11195e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 11205e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 11215e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 11225e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 11235e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 1124adae7080SAndrew Gallatin } 1125b2fc195eSAndrew Gallatin 11266d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 11275e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 11285e7d8541SAndrew Gallatin 11295e7d8541SAndrew Gallatin 11305e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 11315e7d8541SAndrew Gallatin 11325e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 11335e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 11345e7d8541SAndrew Gallatin 11355e7d8541SAndrew Gallatin 11365e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 11376d87a65dSAndrew Gallatin &cmd); 11385e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 1139b2fc195eSAndrew Gallatin if (status != 0) { 1140b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 1141b2fc195eSAndrew Gallatin return status; 1142b2fc195eSAndrew Gallatin } 1143b2fc195eSAndrew Gallatin 11445e7d8541SAndrew Gallatin 11455e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 11465e7d8541SAndrew Gallatin 11475e7d8541SAndrew Gallatin 11485e7d8541SAndrew Gallatin /* run a DMA benchmark */ 11498fe615baSAndrew Gallatin (void) mxge_dma_test(sc, MXGEFW_DMA_TEST); 11505e7d8541SAndrew Gallatin 1151b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 11525e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 11535e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 1154b2fc195eSAndrew Gallatin sc->tx.req = 0; 1155b2fc195eSAndrew Gallatin sc->tx.done = 0; 11565e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 1157a82c2581SAndrew Gallatin sc->tx.wake = 0; 1158adae7080SAndrew Gallatin sc->tx_defrag = 0; 1159a82c2581SAndrew Gallatin sc->tx.stall = 0; 1160b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1161b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1162b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 1163a98d6cd7SAndrew Gallatin sc->fw_stats->valid = 0; 1164a98d6cd7SAndrew Gallatin sc->fw_stats->send_done_count = 0; 1165053e637fSAndrew Gallatin sc->lro_bad_csum = 0; 1166053e637fSAndrew Gallatin sc->lro_queued = 0; 1167053e637fSAndrew Gallatin sc->lro_flushed = 0; 11686d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 11696d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 11706d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 11710fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1172b2fc195eSAndrew Gallatin return status; 1173b2fc195eSAndrew Gallatin } 1174b2fc195eSAndrew Gallatin 1175b2fc195eSAndrew Gallatin static int 11766d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1177b2fc195eSAndrew Gallatin { 11786d87a65dSAndrew Gallatin mxge_softc_t *sc; 1179b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1180b2fc195eSAndrew Gallatin int err; 1181b2fc195eSAndrew Gallatin 1182b2fc195eSAndrew Gallatin sc = arg1; 1183b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1184b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1185b2fc195eSAndrew Gallatin if (err != 0) { 1186b2fc195eSAndrew Gallatin return err; 1187b2fc195eSAndrew Gallatin } 1188b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1189b2fc195eSAndrew Gallatin return 0; 1190b2fc195eSAndrew Gallatin 1191b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1192b2fc195eSAndrew Gallatin return EINVAL; 1193b2fc195eSAndrew Gallatin 1194a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 11955e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1196b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 11975e7d8541SAndrew Gallatin 1198a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1199b2fc195eSAndrew Gallatin return err; 1200b2fc195eSAndrew Gallatin } 1201b2fc195eSAndrew Gallatin 1202b2fc195eSAndrew Gallatin static int 12036d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1204b2fc195eSAndrew Gallatin { 12056d87a65dSAndrew Gallatin mxge_softc_t *sc; 1206b2fc195eSAndrew Gallatin unsigned int enabled; 1207b2fc195eSAndrew Gallatin int err; 1208b2fc195eSAndrew Gallatin 1209b2fc195eSAndrew Gallatin sc = arg1; 1210b2fc195eSAndrew Gallatin enabled = sc->pause; 1211b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1212b2fc195eSAndrew Gallatin if (err != 0) { 1213b2fc195eSAndrew Gallatin return err; 1214b2fc195eSAndrew Gallatin } 1215b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1216b2fc195eSAndrew Gallatin return 0; 1217b2fc195eSAndrew Gallatin 1218a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 12196d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1220a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1221b2fc195eSAndrew Gallatin return err; 1222b2fc195eSAndrew Gallatin } 1223b2fc195eSAndrew Gallatin 1224b2fc195eSAndrew Gallatin static int 12256d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1226b2fc195eSAndrew Gallatin { 1227b2fc195eSAndrew Gallatin int err; 1228b2fc195eSAndrew Gallatin 1229b2fc195eSAndrew Gallatin if (arg1 == NULL) 1230b2fc195eSAndrew Gallatin return EFAULT; 1231b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1232b2fc195eSAndrew Gallatin arg1 = NULL; 1233b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1234b2fc195eSAndrew Gallatin 1235b2fc195eSAndrew Gallatin return err; 1236b2fc195eSAndrew Gallatin } 1237b2fc195eSAndrew Gallatin 1238b2fc195eSAndrew Gallatin static void 12396d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1240b2fc195eSAndrew Gallatin { 1241b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1242b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 12435e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1244b2fc195eSAndrew Gallatin 1245b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1246b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1247b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1248b2fc195eSAndrew Gallatin 12495e7d8541SAndrew Gallatin /* random information */ 12505e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12515e7d8541SAndrew Gallatin "firmware_version", 12525e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 12535e7d8541SAndrew Gallatin 0, "firmware version"); 12545e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12555e7d8541SAndrew Gallatin "serial_number", 12565e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 12575e7d8541SAndrew Gallatin 0, "serial number"); 12585e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12595e7d8541SAndrew Gallatin "product_code", 12605e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 12615e7d8541SAndrew Gallatin 0, "product_code"); 12625e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1263d91b1b49SAndrew Gallatin "pcie_link_width", 1264d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1265d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1266d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12675e7d8541SAndrew Gallatin "tx_boundary", 12685e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 12695e7d8541SAndrew Gallatin 0, "tx_boundary"); 12705e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1271091feecdSAndrew Gallatin "write_combine", 1272091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1273091feecdSAndrew Gallatin 0, "write combining PIO?"); 1274091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12755e7d8541SAndrew Gallatin "read_dma_MBs", 12765e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 12775e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 12785e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12795e7d8541SAndrew Gallatin "write_dma_MBs", 12805e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 12815e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 12825e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12835e7d8541SAndrew Gallatin "read_write_dma_MBs", 12845e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 12855e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 12865e7d8541SAndrew Gallatin 12875e7d8541SAndrew Gallatin 12885e7d8541SAndrew Gallatin /* performance related tunables */ 1289b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1290b2fc195eSAndrew Gallatin "intr_coal_delay", 1291b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 12926d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1293b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1294b2fc195eSAndrew Gallatin 1295b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1296b2fc195eSAndrew Gallatin "flow_control_enabled", 1297b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 12986d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1299b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1300b2fc195eSAndrew Gallatin 1301b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13025e7d8541SAndrew Gallatin "deassert_wait", 13035e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 13045e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1305b2fc195eSAndrew Gallatin 1306b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1307b2fc195eSAndrew Gallatin Need to swap it */ 1308b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1309b2fc195eSAndrew Gallatin "link_up", 1310b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 13116d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1312b2fc195eSAndrew Gallatin "I", "link up"); 1313b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1314b2fc195eSAndrew Gallatin "rdma_tags_available", 1315b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 13166d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1317b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1318b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1319adae7080SAndrew Gallatin "dropped_bad_crc32", 1320adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1321adae7080SAndrew Gallatin &fw->dropped_bad_crc32, 13226d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1323adae7080SAndrew Gallatin "I", "dropped_bad_crc32"); 1324adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1325adae7080SAndrew Gallatin "dropped_bad_phy", 1326adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1327adae7080SAndrew Gallatin &fw->dropped_bad_phy, 1328adae7080SAndrew Gallatin 0, mxge_handle_be32, 1329adae7080SAndrew Gallatin "I", "dropped_bad_phy"); 1330b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1331b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1332b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1333b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 13346d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1335b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1336b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1337adae7080SAndrew Gallatin "dropped_link_overflow", 1338adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 1339adae7080SAndrew Gallatin 0, mxge_handle_be32, 1340adae7080SAndrew Gallatin "I", "dropped_link_overflow"); 1341adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 13420fa7f681SAndrew Gallatin "dropped_multicast_filtered", 13430fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 13440fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 13450fa7f681SAndrew Gallatin 0, mxge_handle_be32, 13460fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 13470fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1348adae7080SAndrew Gallatin "dropped_no_big_buffer", 1349adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 13506d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1351adae7080SAndrew Gallatin "I", "dropped_no_big_buffer"); 1352b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1353b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1354b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1355b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 13566d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1357b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1358b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1359adae7080SAndrew Gallatin "dropped_overrun", 1360adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 13616d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1362adae7080SAndrew Gallatin "I", "dropped_overrun"); 1363adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1364adae7080SAndrew Gallatin "dropped_pause", 1365adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1366adae7080SAndrew Gallatin &fw->dropped_pause, 1367adae7080SAndrew Gallatin 0, mxge_handle_be32, 1368adae7080SAndrew Gallatin "I", "dropped_pause"); 1369adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1370adae7080SAndrew Gallatin "dropped_runt", 1371adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 1372adae7080SAndrew Gallatin 0, mxge_handle_be32, 1373adae7080SAndrew Gallatin "I", "dropped_runt"); 1374b2fc195eSAndrew Gallatin 1375b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1376b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13775e7d8541SAndrew Gallatin "rx_small_cnt", 13785e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 13795e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 13805e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13815e7d8541SAndrew Gallatin "rx_big_cnt", 13825e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 13835e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 13845e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1385b2fc195eSAndrew Gallatin "tx_req", 1386b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1387b2fc195eSAndrew Gallatin 0, "tx_req"); 1388b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1389b2fc195eSAndrew Gallatin "tx_done", 1390b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1391b2fc195eSAndrew Gallatin 0, "tx_done"); 1392b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13935e7d8541SAndrew Gallatin "tx_pkt_done", 13945e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 13955e7d8541SAndrew Gallatin 0, "tx_done"); 1396a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1397a82c2581SAndrew Gallatin "tx_stall", 1398a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.stall, 1399a82c2581SAndrew Gallatin 0, "tx_stall"); 1400a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1401a82c2581SAndrew Gallatin "tx_wake", 1402a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.wake, 1403a82c2581SAndrew Gallatin 0, "tx_wake"); 1404adae7080SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1405adae7080SAndrew Gallatin "tx_defrag", 1406adae7080SAndrew Gallatin CTLFLAG_RD, &sc->tx_defrag, 1407adae7080SAndrew Gallatin 0, "tx_defrag"); 14085e7d8541SAndrew Gallatin 14095e7d8541SAndrew Gallatin /* verbose printing? */ 1410b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14115e7d8541SAndrew Gallatin "verbose", 14125e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 14135e7d8541SAndrew Gallatin 0, "verbose printing"); 1414b2fc195eSAndrew Gallatin 1415053e637fSAndrew Gallatin /* lro */ 1416053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1417adae7080SAndrew Gallatin "lro_cnt", CTLFLAG_RD, &sc->lro_cnt, 1418053e637fSAndrew Gallatin 0, "number of lro merge queues"); 1419053e637fSAndrew Gallatin 1420053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1421053e637fSAndrew Gallatin "lro_flushed", CTLFLAG_RD, &sc->lro_flushed, 1422053e637fSAndrew Gallatin 0, "number of lro merge queues flushed"); 1423053e637fSAndrew Gallatin 1424053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1425053e637fSAndrew Gallatin "lro_queued", CTLFLAG_RD, &sc->lro_queued, 1426053e637fSAndrew Gallatin 0, "number of frames appended to lro merge queues"); 1427053e637fSAndrew Gallatin 1428b2fc195eSAndrew Gallatin } 1429b2fc195eSAndrew Gallatin 1430b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1431b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1432b2fc195eSAndrew Gallatin 1433b2fc195eSAndrew Gallatin static inline void 14346d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1435b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1436b2fc195eSAndrew Gallatin { 1437b2fc195eSAndrew Gallatin int idx, starting_slot; 1438b2fc195eSAndrew Gallatin starting_slot = tx->req; 1439b2fc195eSAndrew Gallatin while (cnt > 1) { 1440b2fc195eSAndrew Gallatin cnt--; 1441b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 14426d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1443b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1444b2fc195eSAndrew Gallatin mb(); 1445b2fc195eSAndrew Gallatin } 1446b2fc195eSAndrew Gallatin } 1447b2fc195eSAndrew Gallatin 1448b2fc195eSAndrew Gallatin /* 1449b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1450b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1451b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1452b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1453b2fc195eSAndrew Gallatin */ 1454b2fc195eSAndrew Gallatin 1455b2fc195eSAndrew Gallatin static inline void 14566d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1457b2fc195eSAndrew Gallatin int cnt) 1458b2fc195eSAndrew Gallatin { 1459b2fc195eSAndrew Gallatin int idx, i; 1460b2fc195eSAndrew Gallatin uint32_t *src_ints; 1461b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1462b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1463b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 14645e7d8541SAndrew Gallatin uint8_t last_flags; 1465b2fc195eSAndrew Gallatin 1466b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1467b2fc195eSAndrew Gallatin 14685e7d8541SAndrew Gallatin last_flags = src->flags; 14695e7d8541SAndrew Gallatin src->flags = 0; 1470b2fc195eSAndrew Gallatin mb(); 1471b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1472b2fc195eSAndrew Gallatin srcp = src; 1473b2fc195eSAndrew Gallatin 1474b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1475b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 14766d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1477b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1478b2fc195eSAndrew Gallatin srcp += 2; 1479b2fc195eSAndrew Gallatin dstp += 2; 1480b2fc195eSAndrew Gallatin } 1481b2fc195eSAndrew Gallatin } else { 1482b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1483b2fc195eSAndrew Gallatin that it is submitted below */ 14846d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1485b2fc195eSAndrew Gallatin i = 0; 1486b2fc195eSAndrew Gallatin } 1487b2fc195eSAndrew Gallatin if (i < cnt) { 1488b2fc195eSAndrew Gallatin /* submit the first request */ 14896d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1490b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1491b2fc195eSAndrew Gallatin } 1492b2fc195eSAndrew Gallatin 1493b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 14945e7d8541SAndrew Gallatin src->flags = last_flags; 1495b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1496b2fc195eSAndrew Gallatin src_ints+=3; 1497b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1498b2fc195eSAndrew Gallatin dst_ints+=3; 1499b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1500b2fc195eSAndrew Gallatin tx->req += cnt; 1501b2fc195eSAndrew Gallatin mb(); 1502b2fc195eSAndrew Gallatin } 1503b2fc195eSAndrew Gallatin 1504b2fc195eSAndrew Gallatin static void 1505c792928fSAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt, 1506c792928fSAndrew Gallatin int ip_off) 1507aed8e389SAndrew Gallatin { 1508aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1509aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1510aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1511aed8e389SAndrew Gallatin struct ip *ip; 1512aed8e389SAndrew Gallatin struct tcphdr *tcp; 1513aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1514aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1515aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1516aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1517aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1518aed8e389SAndrew Gallatin static int once; 1519aed8e389SAndrew Gallatin 1520aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1521aed8e389SAndrew Gallatin 1522aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1523aed8e389SAndrew Gallatin * send loop that we are still in the 1524aed8e389SAndrew Gallatin * header portion of the TSO packet. 1525aed8e389SAndrew Gallatin */ 1526aed8e389SAndrew Gallatin 1527aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1528aed8e389SAndrew Gallatin header together in the first mbuf, copy 1529aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1530c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { 1531c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + sizeof (*ip), 1532aed8e389SAndrew Gallatin sc->scratch); 1533c792928fSAndrew Gallatin ip = (struct ip *)(sc->scratch + ip_off); 1534aed8e389SAndrew Gallatin } else { 1535c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1536aed8e389SAndrew Gallatin } 1537c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2) 1538aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1539c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + (ip->ip_hl << 2) 1540aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1541c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1542aed8e389SAndrew Gallatin } 1543aed8e389SAndrew Gallatin 1544aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1545c792928fSAndrew Gallatin cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2)); 1546aed8e389SAndrew Gallatin 1547aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1548c792928fSAndrew Gallatin cksum_offset = ip_off + (ip->ip_hl << 2); 1549aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1550aed8e389SAndrew Gallatin 1551aed8e389SAndrew Gallatin 1552aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1553aed8e389SAndrew Gallatin * The firmware figures out where to put 1554aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1555aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1556aed8e389SAndrew Gallatin 1557aed8e389SAndrew Gallatin tx = &sc->tx; 1558aed8e389SAndrew Gallatin req = tx->req_list; 1559aed8e389SAndrew Gallatin seg = tx->seg_list; 1560aed8e389SAndrew Gallatin cnt = 0; 1561aed8e389SAndrew Gallatin rdma_count = 0; 1562aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1563aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1564aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1565aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1566aed8e389SAndrew Gallatin * to 0 after a segment cut. 1567aed8e389SAndrew Gallatin * 1568aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1569aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1570aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1571aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1572aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1573aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1574aed8e389SAndrew Gallatin * 1575aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1576aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1577aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1578aed8e389SAndrew Gallatin */ 1579aed8e389SAndrew Gallatin 1580aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1581aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1582aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1583aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1584e39a0a37SAndrew Gallatin len = seg->ds_len; 1585aed8e389SAndrew Gallatin 1586aed8e389SAndrew Gallatin while (len) { 1587aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1588e39a0a37SAndrew Gallatin seglen = len; 1589aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1590aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1591aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1592aed8e389SAndrew Gallatin /* payload */ 1593aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1594aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1595aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1596aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1597aed8e389SAndrew Gallatin flags_next |= next_is_first * 1598aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1599aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1600aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1601aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1602aed8e389SAndrew Gallatin /* header ends */ 1603aed8e389SAndrew Gallatin rdma_count = -1; 1604aed8e389SAndrew Gallatin cum_len_next = 0; 1605aed8e389SAndrew Gallatin seglen = -cum_len; 1606aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1607aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1608aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1609aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1610aed8e389SAndrew Gallatin } 1611aed8e389SAndrew Gallatin 1612aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1613aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1614aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1615aed8e389SAndrew Gallatin req->pad = 0; 1616aed8e389SAndrew Gallatin req->rdma_count = 1; 1617aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1618aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1619aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1620aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1621aed8e389SAndrew Gallatin low += seglen; 1622aed8e389SAndrew Gallatin len -= seglen; 1623aed8e389SAndrew Gallatin cum_len = cum_len_next; 1624aed8e389SAndrew Gallatin flags = flags_next; 1625aed8e389SAndrew Gallatin req++; 1626aed8e389SAndrew Gallatin cnt++; 1627aed8e389SAndrew Gallatin rdma_count++; 1628aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1629aed8e389SAndrew Gallatin cksum_offset -= seglen; 1630aed8e389SAndrew Gallatin else 1631aed8e389SAndrew Gallatin cksum_offset = 0; 1632adae7080SAndrew Gallatin if (__predict_false(cnt > tx->max_desc)) 1633aed8e389SAndrew Gallatin goto drop; 1634aed8e389SAndrew Gallatin } 1635aed8e389SAndrew Gallatin busdma_seg_cnt--; 1636aed8e389SAndrew Gallatin seg++; 1637aed8e389SAndrew Gallatin } 1638aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1639aed8e389SAndrew Gallatin 1640aed8e389SAndrew Gallatin do { 1641aed8e389SAndrew Gallatin req--; 1642aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1643aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1644aed8e389SAndrew Gallatin 1645aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1646aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1647aed8e389SAndrew Gallatin return; 1648aed8e389SAndrew Gallatin 1649aed8e389SAndrew Gallatin drop: 1650e39a0a37SAndrew Gallatin bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map); 1651aed8e389SAndrew Gallatin m_freem(m); 1652aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1653aed8e389SAndrew Gallatin if (!once) { 1654adae7080SAndrew Gallatin printf("tx->max_desc exceeded via TSO!\n"); 1655adae7080SAndrew Gallatin printf("mss = %d, %ld, %d!\n", mss, 1656adae7080SAndrew Gallatin (long)seg - (long)tx->seg_list, tx->max_desc); 1657aed8e389SAndrew Gallatin once = 1; 1658aed8e389SAndrew Gallatin } 1659aed8e389SAndrew Gallatin return; 1660aed8e389SAndrew Gallatin 1661aed8e389SAndrew Gallatin } 1662aed8e389SAndrew Gallatin 1663c792928fSAndrew Gallatin /* 1664c792928fSAndrew Gallatin * We reproduce the software vlan tag insertion from 1665c792928fSAndrew Gallatin * net/if_vlan.c:vlan_start() here so that we can advertise "hardware" 1666c792928fSAndrew Gallatin * vlan tag insertion. We need to advertise this in order to have the 1667c792928fSAndrew Gallatin * vlan interface respect our csum offload flags. 1668c792928fSAndrew Gallatin */ 1669c792928fSAndrew Gallatin static struct mbuf * 1670c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m) 1671c792928fSAndrew Gallatin { 1672c792928fSAndrew Gallatin struct ether_vlan_header *evl; 1673c792928fSAndrew Gallatin 1674c792928fSAndrew Gallatin M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); 1675c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1676c792928fSAndrew Gallatin return NULL; 1677c792928fSAndrew Gallatin if (m->m_len < sizeof(*evl)) { 1678c792928fSAndrew Gallatin m = m_pullup(m, sizeof(*evl)); 1679c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1680c792928fSAndrew Gallatin return NULL; 1681c792928fSAndrew Gallatin } 1682c792928fSAndrew Gallatin /* 1683c792928fSAndrew Gallatin * Transform the Ethernet header into an Ethernet header 1684c792928fSAndrew Gallatin * with 802.1Q encapsulation. 1685c792928fSAndrew Gallatin */ 1686c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 1687c792928fSAndrew Gallatin bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, 1688c792928fSAndrew Gallatin (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); 1689c792928fSAndrew Gallatin evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 1690c792928fSAndrew Gallatin evl->evl_tag = htons(m->m_pkthdr.ether_vtag); 1691c792928fSAndrew Gallatin m->m_flags &= ~M_VLANTAG; 1692c792928fSAndrew Gallatin return m; 1693c792928fSAndrew Gallatin } 1694c792928fSAndrew Gallatin 1695aed8e389SAndrew Gallatin static void 16966d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1697b2fc195eSAndrew Gallatin { 1698b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1699b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1700b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1701b2fc195eSAndrew Gallatin struct ifnet *ifp; 17026d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1703b2fc195eSAndrew Gallatin struct ip *ip; 1704c792928fSAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag, ip_off; 1705aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1706aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1707b2fc195eSAndrew Gallatin 1708b2fc195eSAndrew Gallatin 1709b2fc195eSAndrew Gallatin 1710b2fc195eSAndrew Gallatin ifp = sc->ifp; 1711b2fc195eSAndrew Gallatin tx = &sc->tx; 1712b2fc195eSAndrew Gallatin 1713c792928fSAndrew Gallatin ip_off = sizeof (struct ether_header); 1714c792928fSAndrew Gallatin if (m->m_flags & M_VLANTAG) { 1715c792928fSAndrew Gallatin m = mxge_vlan_tag_insert(m); 1716c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1717c792928fSAndrew Gallatin goto drop; 1718c792928fSAndrew Gallatin ip_off += ETHER_VLAN_ENCAP_LEN; 1719c792928fSAndrew Gallatin } 1720c792928fSAndrew Gallatin 1721b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1722b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1723b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1724aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1725b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1726adae7080SAndrew Gallatin if (__predict_false(err == EFBIG)) { 1727b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1728b2fc195eSAndrew Gallatin to defrag */ 1729b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1730b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1731b2fc195eSAndrew Gallatin goto drop; 1732b2fc195eSAndrew Gallatin } 1733adae7080SAndrew Gallatin sc->tx_defrag++; 1734b2fc195eSAndrew Gallatin m = m_tmp; 1735b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1736b2fc195eSAndrew Gallatin tx->info[idx].map, 1737aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1738b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1739b2fc195eSAndrew Gallatin } 1740adae7080SAndrew Gallatin if (__predict_false(err != 0)) { 1741aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1742aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1743b2fc195eSAndrew Gallatin goto drop; 1744b2fc195eSAndrew Gallatin } 1745b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1746b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 17475e7d8541SAndrew Gallatin tx->info[idx].m = m; 1748b2fc195eSAndrew Gallatin 1749aed8e389SAndrew Gallatin 1750aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1751aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1752c792928fSAndrew Gallatin mxge_encap_tso(sc, m, cnt, ip_off); 1753aed8e389SAndrew Gallatin return; 1754aed8e389SAndrew Gallatin } 1755aed8e389SAndrew Gallatin 1756b2fc195eSAndrew Gallatin req = tx->req_list; 1757b2fc195eSAndrew Gallatin cksum_offset = 0; 17585e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 17595e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1760b2fc195eSAndrew Gallatin 1761b2fc195eSAndrew Gallatin /* checksum offloading? */ 1762b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1763aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1764aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1765c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { 1766c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + sizeof (*ip), 1767aed8e389SAndrew Gallatin sc->scratch); 1768c792928fSAndrew Gallatin ip = (struct ip *)(sc->scratch + ip_off); 1769aed8e389SAndrew Gallatin } else { 1770c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1771aed8e389SAndrew Gallatin } 1772c792928fSAndrew Gallatin cksum_offset = ip_off + (ip->ip_hl << 2); 1773b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 17745e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1775b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 17765e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1777aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1778aed8e389SAndrew Gallatin } else { 1779aed8e389SAndrew Gallatin odd_flag = 0; 1780b2fc195eSAndrew Gallatin } 17815e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 17825e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1783b2fc195eSAndrew Gallatin 1784b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1785b2fc195eSAndrew Gallatin cum_len = 0; 1786aed8e389SAndrew Gallatin seg = tx->seg_list; 17875e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1788b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1789b2fc195eSAndrew Gallatin req->addr_low = 17906d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1791b2fc195eSAndrew Gallatin req->addr_high = 17926d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1793b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1794b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1795b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1796b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1797b2fc195eSAndrew Gallatin else 1798b2fc195eSAndrew Gallatin cksum_offset = 0; 17995e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 18005e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 18015e7d8541SAndrew Gallatin req->rdma_count = 1; 1802aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1803b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1804b2fc195eSAndrew Gallatin seg++; 1805b2fc195eSAndrew Gallatin req++; 1806b2fc195eSAndrew Gallatin req->flags = 0; 1807b2fc195eSAndrew Gallatin } 1808b2fc195eSAndrew Gallatin req--; 1809b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1810b2fc195eSAndrew Gallatin if (cum_len < 60) { 1811b2fc195eSAndrew Gallatin req++; 1812b2fc195eSAndrew Gallatin req->addr_low = 18136d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1814b2fc195eSAndrew Gallatin req->addr_high = 18156d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1816b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 18175e7d8541SAndrew Gallatin req->cksum_offset = 0; 18185e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 18195e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 18205e7d8541SAndrew Gallatin req->rdma_count = 1; 1821aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1822b2fc195eSAndrew Gallatin cnt++; 1823b2fc195eSAndrew Gallatin } 18245e7d8541SAndrew Gallatin 18255e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 18265e7d8541SAndrew Gallatin #if 0 18275e7d8541SAndrew Gallatin /* print what the firmware will see */ 18285e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 18295e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 18305e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 18315e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 18325e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 18335e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 18345e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 18355e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 18365e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 18375e7d8541SAndrew Gallatin } 18385e7d8541SAndrew Gallatin printf("--------------\n"); 18395e7d8541SAndrew Gallatin #endif 18405e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 18416d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1842b2fc195eSAndrew Gallatin return; 1843b2fc195eSAndrew Gallatin 1844b2fc195eSAndrew Gallatin drop: 1845b2fc195eSAndrew Gallatin m_freem(m); 1846b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1847b2fc195eSAndrew Gallatin return; 1848b2fc195eSAndrew Gallatin } 1849b2fc195eSAndrew Gallatin 1850b2fc195eSAndrew Gallatin 18516d914a32SAndrew Gallatin 18526d914a32SAndrew Gallatin 18536d914a32SAndrew Gallatin static inline void 18546d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1855b2fc195eSAndrew Gallatin { 1856b2fc195eSAndrew Gallatin struct mbuf *m; 1857b2fc195eSAndrew Gallatin struct ifnet *ifp; 1858adae7080SAndrew Gallatin mxge_tx_buf_t *tx; 1859b2fc195eSAndrew Gallatin 1860b2fc195eSAndrew Gallatin ifp = sc->ifp; 1861adae7080SAndrew Gallatin tx = &sc->tx; 1862adae7080SAndrew Gallatin while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) { 18636d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 18646d914a32SAndrew Gallatin if (m == NULL) { 18656d914a32SAndrew Gallatin return; 18666d914a32SAndrew Gallatin } 1867b2fc195eSAndrew Gallatin /* let BPF see it */ 1868b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1869b2fc195eSAndrew Gallatin 1870b2fc195eSAndrew Gallatin /* give it to the nic */ 18716d87a65dSAndrew Gallatin mxge_encap(sc, m); 18726d914a32SAndrew Gallatin } 18736d914a32SAndrew Gallatin /* ran out of transmit slots */ 1874a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 1875b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1876adae7080SAndrew Gallatin tx->stall++; 1877a82c2581SAndrew Gallatin } 1878b2fc195eSAndrew Gallatin } 1879b2fc195eSAndrew Gallatin 1880b2fc195eSAndrew Gallatin static void 18816d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1882b2fc195eSAndrew Gallatin { 18836d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1884b2fc195eSAndrew Gallatin 1885b2fc195eSAndrew Gallatin 1886a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 18876d87a65dSAndrew Gallatin mxge_start_locked(sc); 1888a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 1889b2fc195eSAndrew Gallatin } 1890b2fc195eSAndrew Gallatin 18915e7d8541SAndrew Gallatin /* 18925e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 18935e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 18945e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 18955e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 18965e7d8541SAndrew Gallatin * in a burst 18975e7d8541SAndrew Gallatin */ 18985e7d8541SAndrew Gallatin static inline void 18995e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 19005e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 19015e7d8541SAndrew Gallatin { 19025e7d8541SAndrew Gallatin uint32_t low; 19035e7d8541SAndrew Gallatin 19045e7d8541SAndrew Gallatin low = src->addr_low; 19055e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 1906a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 1907a1480dfbSAndrew Gallatin mb(); 1908a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 19095e7d8541SAndrew Gallatin mb(); 191040385a5fSAndrew Gallatin src->addr_low = low; 19115e7d8541SAndrew Gallatin dst->addr_low = low; 19125e7d8541SAndrew Gallatin mb(); 19135e7d8541SAndrew Gallatin } 19145e7d8541SAndrew Gallatin 1915b2fc195eSAndrew Gallatin static int 19166d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1917b2fc195eSAndrew Gallatin { 1918b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1919b2fc195eSAndrew Gallatin struct mbuf *m; 19206d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1921b2fc195eSAndrew Gallatin int cnt, err; 1922b2fc195eSAndrew Gallatin 1923b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1924b2fc195eSAndrew Gallatin if (m == NULL) { 1925b2fc195eSAndrew Gallatin rx->alloc_fail++; 1926b2fc195eSAndrew Gallatin err = ENOBUFS; 1927b2fc195eSAndrew Gallatin goto done; 1928b2fc195eSAndrew Gallatin } 1929b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1930b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1931b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1932b2fc195eSAndrew Gallatin if (err != 0) { 1933b2fc195eSAndrew Gallatin m_free(m); 1934b2fc195eSAndrew Gallatin goto done; 1935b2fc195eSAndrew Gallatin } 1936b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1937b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 19386d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1939b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 19406d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1941b2fc195eSAndrew Gallatin 1942b2fc195eSAndrew Gallatin done: 1943adae7080SAndrew Gallatin if ((idx & 7) == 7) 1944adae7080SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]); 1945b2fc195eSAndrew Gallatin return err; 1946b2fc195eSAndrew Gallatin } 1947b2fc195eSAndrew Gallatin 1948b2fc195eSAndrew Gallatin static int 19496d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1950b2fc195eSAndrew Gallatin { 1951053e637fSAndrew Gallatin bus_dma_segment_t seg[3]; 1952b2fc195eSAndrew Gallatin struct mbuf *m; 19536d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1954053e637fSAndrew Gallatin int cnt, err, i; 1955b2fc195eSAndrew Gallatin 1956053e637fSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size); 1957b2fc195eSAndrew Gallatin if (m == NULL) { 1958b2fc195eSAndrew Gallatin rx->alloc_fail++; 1959b2fc195eSAndrew Gallatin err = ENOBUFS; 1960b2fc195eSAndrew Gallatin goto done; 1961b2fc195eSAndrew Gallatin } 1962053e637fSAndrew Gallatin m->m_len = rx->cl_size; 1963b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1964053e637fSAndrew Gallatin seg, &cnt, BUS_DMA_NOWAIT); 1965b2fc195eSAndrew Gallatin if (err != 0) { 1966b2fc195eSAndrew Gallatin m_free(m); 1967b2fc195eSAndrew Gallatin goto done; 1968b2fc195eSAndrew Gallatin } 1969b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1970053e637fSAndrew Gallatin 1971053e637fSAndrew Gallatin for (i = 0; i < cnt; i++) { 1972053e637fSAndrew Gallatin rx->shadow[idx + i].addr_low = 1973053e637fSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr)); 1974053e637fSAndrew Gallatin rx->shadow[idx + i].addr_high = 1975053e637fSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr)); 1976053e637fSAndrew Gallatin } 1977053e637fSAndrew Gallatin 1978b2fc195eSAndrew Gallatin 1979b2fc195eSAndrew Gallatin done: 1980053e637fSAndrew Gallatin for (i = 0; i < rx->nbufs; i++) { 1981b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 19825e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 19835e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 1984b2fc195eSAndrew Gallatin } 1985053e637fSAndrew Gallatin idx++; 1986053e637fSAndrew Gallatin } 1987b2fc195eSAndrew Gallatin return err; 1988b2fc195eSAndrew Gallatin } 1989b2fc195eSAndrew Gallatin 19909b03b0f3SAndrew Gallatin /* 19919b03b0f3SAndrew Gallatin * Myri10GE hardware checksums are not valid if the sender 19929b03b0f3SAndrew Gallatin * padded the frame with non-zero padding. This is because 19939b03b0f3SAndrew Gallatin * the firmware just does a simple 16-bit 1s complement 19949b03b0f3SAndrew Gallatin * checksum across the entire frame, excluding the first 14 1995053e637fSAndrew Gallatin * bytes. It is best to simply to check the checksum and 1996053e637fSAndrew Gallatin * tell the stack about it only if the checksum is good 19979b03b0f3SAndrew Gallatin */ 19989b03b0f3SAndrew Gallatin 1999053e637fSAndrew Gallatin static inline uint16_t 2000053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 2001053e637fSAndrew Gallatin { 2002053e637fSAndrew Gallatin struct ether_header *eh; 2003053e637fSAndrew Gallatin struct ip *ip; 2004053e637fSAndrew Gallatin uint16_t c; 2005053e637fSAndrew Gallatin 2006053e637fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2007053e637fSAndrew Gallatin 2008053e637fSAndrew Gallatin /* only deal with IPv4 TCP & UDP for now */ 2009053e637fSAndrew Gallatin if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP))) 2010053e637fSAndrew Gallatin return 1; 2011053e637fSAndrew Gallatin ip = (struct ip *)(eh + 1); 2012053e637fSAndrew Gallatin if (__predict_false(ip->ip_p != IPPROTO_TCP && 2013053e637fSAndrew Gallatin ip->ip_p != IPPROTO_UDP)) 2014053e637fSAndrew Gallatin return 1; 2015053e637fSAndrew Gallatin 2016053e637fSAndrew Gallatin c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 2017053e637fSAndrew Gallatin htonl(ntohs(csum) + ntohs(ip->ip_len) + 2018053e637fSAndrew Gallatin - (ip->ip_hl << 2) + ip->ip_p)); 2019053e637fSAndrew Gallatin c ^= 0xffff; 2020053e637fSAndrew Gallatin return (c); 20215e7d8541SAndrew Gallatin } 2022053e637fSAndrew Gallatin 2023c792928fSAndrew Gallatin static void 2024c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum) 2025c792928fSAndrew Gallatin { 2026c792928fSAndrew Gallatin struct ether_vlan_header *evl; 2027c792928fSAndrew Gallatin struct ether_header *eh; 2028c792928fSAndrew Gallatin uint32_t partial; 2029c792928fSAndrew Gallatin 2030c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 2031c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2032c792928fSAndrew Gallatin 2033c792928fSAndrew Gallatin /* 2034c792928fSAndrew Gallatin * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes 2035c792928fSAndrew Gallatin * after what the firmware thought was the end of the ethernet 2036c792928fSAndrew Gallatin * header. 2037c792928fSAndrew Gallatin */ 2038c792928fSAndrew Gallatin 2039c792928fSAndrew Gallatin /* put checksum into host byte order */ 2040c792928fSAndrew Gallatin *csum = ntohs(*csum); 2041c792928fSAndrew Gallatin partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN)); 2042c792928fSAndrew Gallatin (*csum) += ~partial; 2043c792928fSAndrew Gallatin (*csum) += ((*csum) < ~partial); 2044c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2045c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2046c792928fSAndrew Gallatin 2047c792928fSAndrew Gallatin /* restore checksum to network byte order; 2048c792928fSAndrew Gallatin later consumers expect this */ 2049c792928fSAndrew Gallatin *csum = htons(*csum); 2050c792928fSAndrew Gallatin 2051c792928fSAndrew Gallatin /* save the tag */ 2052c792928fSAndrew Gallatin m->m_flags |= M_VLANTAG; 2053c792928fSAndrew Gallatin m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); 2054c792928fSAndrew Gallatin 2055c792928fSAndrew Gallatin /* 2056c792928fSAndrew Gallatin * Remove the 802.1q header by copying the Ethernet 2057c792928fSAndrew Gallatin * addresses over it and adjusting the beginning of 2058c792928fSAndrew Gallatin * the data in the mbuf. The encapsulated Ethernet 2059c792928fSAndrew Gallatin * type field is already in place. 2060c792928fSAndrew Gallatin */ 2061c792928fSAndrew Gallatin bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, 2062c792928fSAndrew Gallatin ETHER_HDR_LEN - ETHER_TYPE_LEN); 2063c792928fSAndrew Gallatin m_adj(m, ETHER_VLAN_ENCAP_LEN); 2064c792928fSAndrew Gallatin } 2065c792928fSAndrew Gallatin 20665e7d8541SAndrew Gallatin 20675e7d8541SAndrew Gallatin static inline void 2068053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2069b2fc195eSAndrew Gallatin { 2070b2fc195eSAndrew Gallatin struct ifnet *ifp; 2071053e637fSAndrew Gallatin struct mbuf *m; 2072c792928fSAndrew Gallatin struct ether_header *eh; 20736d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2074053e637fSAndrew Gallatin bus_dmamap_t old_map; 2075b2fc195eSAndrew Gallatin int idx; 2076053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2077b2fc195eSAndrew Gallatin 2078b2fc195eSAndrew Gallatin ifp = sc->ifp; 2079053e637fSAndrew Gallatin rx = &sc->rx_big; 2080b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2081053e637fSAndrew Gallatin rx->cnt += rx->nbufs; 2082b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2083b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2084b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 20856d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 2086053e637fSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2087053e637fSAndrew Gallatin ifp->if_ierrors++; 2088053e637fSAndrew Gallatin return; 2089b2fc195eSAndrew Gallatin } 2090053e637fSAndrew Gallatin 2091b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2092b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2093b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2094b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2095b2fc195eSAndrew Gallatin 2096b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2097b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2098b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2099b2fc195eSAndrew Gallatin 2100053e637fSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2101053e637fSAndrew Gallatin * aligned */ 21025e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2103b2fc195eSAndrew Gallatin 2104053e637fSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 2105053e637fSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 21069b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2107c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2108c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2109c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2110c792928fSAndrew Gallatin } 2111b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2112053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2113053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2114b2fc195eSAndrew Gallatin return; 2115053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2116053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2117053e637fSAndrew Gallatin checksum is good */ 2118053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2119053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2120b2fc195eSAndrew Gallatin } 2121053e637fSAndrew Gallatin /* pass the frame up the stack */ 2122053e637fSAndrew Gallatin (*ifp->if_input)(ifp, m); 2123b2fc195eSAndrew Gallatin } 2124b2fc195eSAndrew Gallatin 2125b2fc195eSAndrew Gallatin static inline void 21265e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2127b2fc195eSAndrew Gallatin { 2128b2fc195eSAndrew Gallatin struct ifnet *ifp; 2129c792928fSAndrew Gallatin struct ether_header *eh; 2130b2fc195eSAndrew Gallatin struct mbuf *m; 21316d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2132b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 2133b2fc195eSAndrew Gallatin int idx; 2134053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2135b2fc195eSAndrew Gallatin 2136b2fc195eSAndrew Gallatin ifp = sc->ifp; 2137b2fc195eSAndrew Gallatin rx = &sc->rx_small; 2138b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2139b2fc195eSAndrew Gallatin rx->cnt++; 2140b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2141b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2142b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 21436d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 2144b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2145b2fc195eSAndrew Gallatin ifp->if_ierrors++; 2146b2fc195eSAndrew Gallatin return; 2147b2fc195eSAndrew Gallatin } 2148b2fc195eSAndrew Gallatin 2149b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2150b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2151b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2152b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2153b2fc195eSAndrew Gallatin 2154b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2155b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2156b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2157b2fc195eSAndrew Gallatin 2158b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2159b2fc195eSAndrew Gallatin * aligned */ 21605e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2161b2fc195eSAndrew Gallatin 21629b03b0f3SAndrew Gallatin m->m_pkthdr.rcvif = ifp; 21639b03b0f3SAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 21649b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2165c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2166c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2167c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2168c792928fSAndrew Gallatin } 2169b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2170053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2171053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2172053e637fSAndrew Gallatin return; 2173053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2174053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2175053e637fSAndrew Gallatin checksum is good */ 2176053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2177053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2178053e637fSAndrew Gallatin } 2179b2fc195eSAndrew Gallatin 2180b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 2181b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 2182b2fc195eSAndrew Gallatin } 2183b2fc195eSAndrew Gallatin 2184b2fc195eSAndrew Gallatin static inline void 21855e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 21865e7d8541SAndrew Gallatin { 21875e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 2188053e637fSAndrew Gallatin struct lro_entry *lro; 21895e7d8541SAndrew Gallatin int limit = 0; 21905e7d8541SAndrew Gallatin uint16_t length; 21915e7d8541SAndrew Gallatin uint16_t checksum; 21925e7d8541SAndrew Gallatin 21935e7d8541SAndrew Gallatin 21945e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 21955e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 21965e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 2197053e637fSAndrew Gallatin checksum = rx_done->entry[rx_done->idx].checksum; 2198b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 21995e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 22005e7d8541SAndrew Gallatin else 22015e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 22025e7d8541SAndrew Gallatin rx_done->cnt++; 2203adae7080SAndrew Gallatin rx_done->idx = rx_done->cnt & rx_done->mask; 22045e7d8541SAndrew Gallatin 22055e7d8541SAndrew Gallatin /* limit potential for livelock */ 2206adae7080SAndrew Gallatin if (__predict_false(++limit > 2 * rx_done->mask)) 22075e7d8541SAndrew Gallatin break; 2208053e637fSAndrew Gallatin } 2209053e637fSAndrew Gallatin while(!SLIST_EMPTY(&sc->lro_active)) { 2210053e637fSAndrew Gallatin lro = SLIST_FIRST(&sc->lro_active); 2211053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_active, next); 2212053e637fSAndrew Gallatin mxge_lro_flush(sc, lro); 22135e7d8541SAndrew Gallatin } 22145e7d8541SAndrew Gallatin } 22155e7d8541SAndrew Gallatin 22165e7d8541SAndrew Gallatin 22175e7d8541SAndrew Gallatin static inline void 22186d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 2219b2fc195eSAndrew Gallatin { 2220b2fc195eSAndrew Gallatin struct ifnet *ifp; 22216d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 2222b2fc195eSAndrew Gallatin struct mbuf *m; 2223b2fc195eSAndrew Gallatin bus_dmamap_t map; 22245e7d8541SAndrew Gallatin int idx, limit; 2225b2fc195eSAndrew Gallatin 22265e7d8541SAndrew Gallatin limit = 0; 2227b2fc195eSAndrew Gallatin tx = &sc->tx; 2228b2fc195eSAndrew Gallatin ifp = sc->ifp; 22295e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2230b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2231b2fc195eSAndrew Gallatin tx->done++; 2232b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2233b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2234b2fc195eSAndrew Gallatin segment per-mbuf */ 2235b2fc195eSAndrew Gallatin if (m != NULL) { 2236b2fc195eSAndrew Gallatin ifp->if_opackets++; 2237b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2238b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2239b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2240b2fc195eSAndrew Gallatin m_freem(m); 2241b2fc195eSAndrew Gallatin } 22425e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 22435e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 22445e7d8541SAndrew Gallatin tx->pkt_done++; 22455e7d8541SAndrew Gallatin } 22465e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 22475e7d8541SAndrew Gallatin 2 full tx rings per call */ 22485e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 22495e7d8541SAndrew Gallatin break; 2250b2fc195eSAndrew Gallatin } 2251b2fc195eSAndrew Gallatin 2252b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2253b2fc195eSAndrew Gallatin its OK to send packets */ 2254b2fc195eSAndrew Gallatin 2255b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2256b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2257a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 2258b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2259a82c2581SAndrew Gallatin sc->tx.wake++; 22606d87a65dSAndrew Gallatin mxge_start_locked(sc); 2261a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 2262b2fc195eSAndrew Gallatin } 2263b2fc195eSAndrew Gallatin } 2264b2fc195eSAndrew Gallatin 2265b2fc195eSAndrew Gallatin static void 22666d87a65dSAndrew Gallatin mxge_intr(void *arg) 2267b2fc195eSAndrew Gallatin { 22686d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 22695e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 22705e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 22715e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 22725e7d8541SAndrew Gallatin uint32_t send_done_count; 22735e7d8541SAndrew Gallatin uint8_t valid; 2274b2fc195eSAndrew Gallatin 2275b2fc195eSAndrew Gallatin 22765e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 22775e7d8541SAndrew Gallatin if (!stats->valid) { 22785e7d8541SAndrew Gallatin return; 2279b2fc195eSAndrew Gallatin } 22805e7d8541SAndrew Gallatin valid = stats->valid; 2281b2fc195eSAndrew Gallatin 2282dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 22835e7d8541SAndrew Gallatin /* lower legacy IRQ */ 22845e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 22855e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 22865e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 22875e7d8541SAndrew Gallatin stats->valid = 0; 2288dc8731d4SAndrew Gallatin } else { 2289dc8731d4SAndrew Gallatin stats->valid = 0; 2290dc8731d4SAndrew Gallatin } 2291dc8731d4SAndrew Gallatin 2292dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 22935e7d8541SAndrew Gallatin do { 22945e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 22955e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 22965e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 22975e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 22985e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 22995e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 23005e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2301b2fc195eSAndrew Gallatin } 23025e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2303b2fc195eSAndrew Gallatin 23045e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 23055e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 23065e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2307b2fc195eSAndrew Gallatin if (sc->link_state) { 23085e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 23095e7d8541SAndrew Gallatin if (mxge_verbose) 23105e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2311b2fc195eSAndrew Gallatin } else { 23125e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 23135e7d8541SAndrew Gallatin if (mxge_verbose) 23145e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2315b2fc195eSAndrew Gallatin } 2316b2fc195eSAndrew Gallatin } 2317b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2318b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2319b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2320b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 23215e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 23225e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 23235e7d8541SAndrew Gallatin } 23245e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2325b2fc195eSAndrew Gallatin } 2326b2fc195eSAndrew Gallatin 23275e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 23285e7d8541SAndrew Gallatin if (valid & 0x1) 23295e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 23305e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2331b2fc195eSAndrew Gallatin } 2332b2fc195eSAndrew Gallatin 2333b2fc195eSAndrew Gallatin static void 23346d87a65dSAndrew Gallatin mxge_init(void *arg) 2335b2fc195eSAndrew Gallatin { 2336b2fc195eSAndrew Gallatin } 2337b2fc195eSAndrew Gallatin 2338b2fc195eSAndrew Gallatin 2339b2fc195eSAndrew Gallatin 2340b2fc195eSAndrew Gallatin static void 23416d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2342b2fc195eSAndrew Gallatin { 2343b2fc195eSAndrew Gallatin int i; 2344b2fc195eSAndrew Gallatin 2345b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2346b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2347b2fc195eSAndrew Gallatin continue; 2348b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2349b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2350b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2351b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2352b2fc195eSAndrew Gallatin } 2353b2fc195eSAndrew Gallatin 23549b03b0f3SAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 23559b03b0f3SAndrew Gallatin if (sc->rx_small.info[i].m == NULL) 2356b2fc195eSAndrew Gallatin continue; 23579b03b0f3SAndrew Gallatin bus_dmamap_unload(sc->rx_small.dmat, 23589b03b0f3SAndrew Gallatin sc->rx_small.info[i].map); 23599b03b0f3SAndrew Gallatin m_freem(sc->rx_small.info[i].m); 23609b03b0f3SAndrew Gallatin sc->rx_small.info[i].m = NULL; 2361b2fc195eSAndrew Gallatin } 2362b2fc195eSAndrew Gallatin 2363b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2364dce01b9bSAndrew Gallatin sc->tx.info[i].flag = 0; 2365b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2366b2fc195eSAndrew Gallatin continue; 2367b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2368b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2369b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2370b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2371b2fc195eSAndrew Gallatin } 2372b2fc195eSAndrew Gallatin } 2373b2fc195eSAndrew Gallatin 2374b2fc195eSAndrew Gallatin static void 23756d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2376b2fc195eSAndrew Gallatin { 2377b2fc195eSAndrew Gallatin int i; 2378b2fc195eSAndrew Gallatin 2379adae7080SAndrew Gallatin if (sc->rx_done.entry != NULL) 2380adae7080SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 2381adae7080SAndrew Gallatin sc->rx_done.entry = NULL; 2382aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2383b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2384aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2385aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2386b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2387b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2388b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2389b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2390b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2391c2657176SAndrew Gallatin if (sc->tx.dmat != NULL) { 2392b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2393b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2394b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2395b2fc195eSAndrew Gallatin } 2396c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2397c2657176SAndrew Gallatin } 2398b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2399b2fc195eSAndrew Gallatin } 2400b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2401c2657176SAndrew Gallatin if (sc->rx_small.dmat != NULL) { 2402b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2403b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2404b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2405b2fc195eSAndrew Gallatin } 2406c2657176SAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2407c2657176SAndrew Gallatin sc->rx_small.extra_map); 2408c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2409c2657176SAndrew Gallatin } 2410b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2411b2fc195eSAndrew Gallatin } 2412b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2413c2657176SAndrew Gallatin if (sc->rx_big.dmat != NULL) { 2414b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2415b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2416b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2417b2fc195eSAndrew Gallatin } 2418b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2419b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2420b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2421b2fc195eSAndrew Gallatin } 2422c2657176SAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2423c2657176SAndrew Gallatin } 2424c2657176SAndrew Gallatin } 2425b2fc195eSAndrew Gallatin 2426b2fc195eSAndrew Gallatin static int 24276d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2428b2fc195eSAndrew Gallatin { 24296d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2430b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2431b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2432b2fc195eSAndrew Gallatin int i, err; 2433b2fc195eSAndrew Gallatin unsigned long bytes; 2434b2fc195eSAndrew Gallatin 2435b2fc195eSAndrew Gallatin /* get ring sizes */ 24365e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2437b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 24385e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2439b2fc195eSAndrew Gallatin if (err != 0) { 2440b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2441b2fc195eSAndrew Gallatin goto abort_with_nothing; 2442b2fc195eSAndrew Gallatin } 2443b2fc195eSAndrew Gallatin 2444b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2445b2fc195eSAndrew Gallatin 2446b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2447b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 244876bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 2449a82c2581SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 245076bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2451b2fc195eSAndrew Gallatin 2452b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2453adae7080SAndrew Gallatin sc->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4); 2454b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2455adae7080SAndrew Gallatin sc->rx_done.mask = (2 * rx_ring_entries) - 1; 2456b2fc195eSAndrew Gallatin 2457b2fc195eSAndrew Gallatin err = ENOMEM; 2458b2fc195eSAndrew Gallatin 2459adae7080SAndrew Gallatin /* allocate interrupt queues */ 2460adae7080SAndrew Gallatin bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); 2461adae7080SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2462adae7080SAndrew Gallatin if (err != 0) 2463adae7080SAndrew Gallatin goto abort_with_nothing; 2464adae7080SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 2465adae7080SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2466adae7080SAndrew Gallatin 2467b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2468b2fc195eSAndrew Gallatin bytes = 8 + 2469adae7080SAndrew Gallatin sizeof (*sc->tx.req_list) * (sc->tx.max_desc + 4); 2470b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2471b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2472adae7080SAndrew Gallatin goto abort_with_alloc; 2473b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2474b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2475b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2476b2fc195eSAndrew Gallatin 2477aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2478adae7080SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * sc->tx.max_desc; 2479aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2480aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2481aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2482aed8e389SAndrew Gallatin goto abort_with_alloc; 2483aed8e389SAndrew Gallatin 2484b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2485b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2486b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2487b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2488b2fc195eSAndrew Gallatin goto abort_with_alloc; 2489b2fc195eSAndrew Gallatin 2490b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2491b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2492b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2493b2fc195eSAndrew Gallatin goto abort_with_alloc; 2494b2fc195eSAndrew Gallatin 2495b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2496b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2497b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2498b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2499b2fc195eSAndrew Gallatin goto abort_with_alloc; 2500b2fc195eSAndrew Gallatin 2501b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2502b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2503b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2504b2fc195eSAndrew Gallatin goto abort_with_alloc; 2505b2fc195eSAndrew Gallatin 2506b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2507b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2508b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2509b2fc195eSAndrew Gallatin goto abort_with_alloc; 2510b2fc195eSAndrew Gallatin 2511b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2512b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2513b2fc195eSAndrew Gallatin 1, /* alignment */ 2514b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2515b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2516b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2517b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2518aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2519adae7080SAndrew Gallatin sc->tx.max_desc - 2, /* num segs */ 2520b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2521b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2522b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2523b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2524b2fc195eSAndrew Gallatin 2525b2fc195eSAndrew Gallatin if (err != 0) { 2526b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2527b2fc195eSAndrew Gallatin err); 2528b2fc195eSAndrew Gallatin goto abort_with_alloc; 2529b2fc195eSAndrew Gallatin } 2530b2fc195eSAndrew Gallatin 2531b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2532b2fc195eSAndrew Gallatin 1, /* alignment */ 2533b2fc195eSAndrew Gallatin 4096, /* boundary */ 2534b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2535b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2536b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2537b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2538b2fc195eSAndrew Gallatin 1, /* num segs */ 2539b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2540b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2541b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2542b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2543b2fc195eSAndrew Gallatin if (err != 0) { 2544b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2545b2fc195eSAndrew Gallatin err); 2546b2fc195eSAndrew Gallatin goto abort_with_alloc; 2547b2fc195eSAndrew Gallatin } 2548b2fc195eSAndrew Gallatin 2549b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2550b2fc195eSAndrew Gallatin 1, /* alignment */ 2551b2fc195eSAndrew Gallatin 4096, /* boundary */ 2552b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2553b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2554b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2555053e637fSAndrew Gallatin 3*4096, /* maxsize */ 2556053e637fSAndrew Gallatin 3, /* num segs */ 2557b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2558b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2559b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2560b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2561b2fc195eSAndrew Gallatin if (err != 0) { 2562b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2563b2fc195eSAndrew Gallatin err); 2564b2fc195eSAndrew Gallatin goto abort_with_alloc; 2565b2fc195eSAndrew Gallatin } 2566b2fc195eSAndrew Gallatin 2567b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2568b2fc195eSAndrew Gallatin in each ring */ 2569b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2570b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2571b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2572b2fc195eSAndrew Gallatin if (err != 0) { 2573b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2574b2fc195eSAndrew Gallatin err); 2575b2fc195eSAndrew Gallatin goto abort_with_alloc; 2576b2fc195eSAndrew Gallatin } 2577b2fc195eSAndrew Gallatin } 2578b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2579b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2580b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2581b2fc195eSAndrew Gallatin if (err != 0) { 2582b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2583b2fc195eSAndrew Gallatin err); 2584b2fc195eSAndrew Gallatin goto abort_with_alloc; 2585b2fc195eSAndrew Gallatin } 2586b2fc195eSAndrew Gallatin } 2587b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2588b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2589b2fc195eSAndrew Gallatin if (err != 0) { 2590b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2591b2fc195eSAndrew Gallatin err); 2592b2fc195eSAndrew Gallatin goto abort_with_alloc; 2593b2fc195eSAndrew Gallatin } 2594b2fc195eSAndrew Gallatin 2595b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2596b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2597b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2598b2fc195eSAndrew Gallatin if (err != 0) { 2599b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2600b2fc195eSAndrew Gallatin err); 2601b2fc195eSAndrew Gallatin goto abort_with_alloc; 2602b2fc195eSAndrew Gallatin } 2603b2fc195eSAndrew Gallatin } 2604b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2605b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2606b2fc195eSAndrew Gallatin if (err != 0) { 2607b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2608b2fc195eSAndrew Gallatin err); 2609b2fc195eSAndrew Gallatin goto abort_with_alloc; 2610b2fc195eSAndrew Gallatin } 2611b2fc195eSAndrew Gallatin return 0; 2612b2fc195eSAndrew Gallatin 2613b2fc195eSAndrew Gallatin abort_with_alloc: 26146d87a65dSAndrew Gallatin mxge_free_rings(sc); 2615b2fc195eSAndrew Gallatin 2616b2fc195eSAndrew Gallatin abort_with_nothing: 2617b2fc195eSAndrew Gallatin return err; 2618b2fc195eSAndrew Gallatin } 2619b2fc195eSAndrew Gallatin 2620053e637fSAndrew Gallatin static void 2621053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs) 2622053e637fSAndrew Gallatin { 2623c792928fSAndrew Gallatin int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD; 2624053e637fSAndrew Gallatin 2625053e637fSAndrew Gallatin if (bufsize < MCLBYTES) { 2626053e637fSAndrew Gallatin /* easy, everything fits in a single buffer */ 2627053e637fSAndrew Gallatin *big_buf_size = MCLBYTES; 2628053e637fSAndrew Gallatin *cl_size = MCLBYTES; 2629053e637fSAndrew Gallatin *nbufs = 1; 2630053e637fSAndrew Gallatin return; 2631053e637fSAndrew Gallatin } 2632053e637fSAndrew Gallatin 2633053e637fSAndrew Gallatin if (bufsize < MJUMPAGESIZE) { 2634053e637fSAndrew Gallatin /* still easy, everything still fits in a single buffer */ 2635053e637fSAndrew Gallatin *big_buf_size = MJUMPAGESIZE; 2636053e637fSAndrew Gallatin *cl_size = MJUMPAGESIZE; 2637053e637fSAndrew Gallatin *nbufs = 1; 2638053e637fSAndrew Gallatin return; 2639053e637fSAndrew Gallatin } 2640053e637fSAndrew Gallatin /* now we need to use virtually contiguous buffers */ 2641053e637fSAndrew Gallatin *cl_size = MJUM9BYTES; 2642053e637fSAndrew Gallatin *big_buf_size = 4096; 2643053e637fSAndrew Gallatin *nbufs = mtu / 4096 + 1; 2644053e637fSAndrew Gallatin /* needs to be a power of two, so round up */ 2645053e637fSAndrew Gallatin if (*nbufs == 3) 2646053e637fSAndrew Gallatin *nbufs = 4; 2647053e637fSAndrew Gallatin } 2648053e637fSAndrew Gallatin 2649b2fc195eSAndrew Gallatin static int 26506d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2651b2fc195eSAndrew Gallatin { 26526d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2653053e637fSAndrew Gallatin int i, err, big_bytes; 2654b2fc195eSAndrew Gallatin bus_dmamap_t map; 26550fa7f681SAndrew Gallatin bus_addr_t bus; 2656053e637fSAndrew Gallatin struct lro_entry *lro_entry; 2657b2fc195eSAndrew Gallatin 2658053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_free); 2659053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_active); 2660053e637fSAndrew Gallatin 2661053e637fSAndrew Gallatin for (i = 0; i < sc->lro_cnt; i++) { 2662053e637fSAndrew Gallatin lro_entry = (struct lro_entry *) 2663053e637fSAndrew Gallatin malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 2664053e637fSAndrew Gallatin if (lro_entry == NULL) { 2665053e637fSAndrew Gallatin sc->lro_cnt = i; 2666053e637fSAndrew Gallatin break; 2667053e637fSAndrew Gallatin } 2668053e637fSAndrew Gallatin SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next); 2669053e637fSAndrew Gallatin } 2670b2fc195eSAndrew Gallatin 26717d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 26727d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 26737d542e2dSAndrew Gallatin 2674adae7080SAndrew Gallatin err = mxge_reset(sc, 1); 2675b2fc195eSAndrew Gallatin if (err != 0) { 2676b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2677b2fc195eSAndrew Gallatin return EIO; 2678b2fc195eSAndrew Gallatin } 2679b2fc195eSAndrew Gallatin 2680053e637fSAndrew Gallatin mxge_choose_params(sc->ifp->if_mtu, &big_bytes, 2681053e637fSAndrew Gallatin &sc->rx_big.cl_size, &sc->rx_big.nbufs); 2682b2fc195eSAndrew Gallatin 2683053e637fSAndrew Gallatin cmd.data0 = sc->rx_big.nbufs; 2684053e637fSAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 2685053e637fSAndrew Gallatin &cmd); 2686053e637fSAndrew Gallatin /* error is only meaningful if we're trying to set 2687053e637fSAndrew Gallatin MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */ 2688053e637fSAndrew Gallatin if (err && sc->rx_big.nbufs > 1) { 2689053e637fSAndrew Gallatin device_printf(sc->dev, 2690053e637fSAndrew Gallatin "Failed to set alway-use-n to %d\n", 2691053e637fSAndrew Gallatin sc->rx_big.nbufs); 2692053e637fSAndrew Gallatin return EIO; 2693053e637fSAndrew Gallatin } 2694b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2695b2fc195eSAndrew Gallatin 26965e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2697b2fc195eSAndrew Gallatin sc->tx.lanai = 2698b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 26996d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 27005e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2701b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2702b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 27035e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2704b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2705b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2706b2fc195eSAndrew Gallatin 2707b2fc195eSAndrew Gallatin if (err != 0) { 2708b2fc195eSAndrew Gallatin device_printf(sc->dev, 2709b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2710a98d6cd7SAndrew Gallatin return EIO; 2711b2fc195eSAndrew Gallatin } 2712b2fc195eSAndrew Gallatin 2713b2fc195eSAndrew Gallatin /* stock receive rings */ 2714b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2715b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 27166d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2717b2fc195eSAndrew Gallatin if (err) { 2718b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2719b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2720b2fc195eSAndrew Gallatin goto abort; 2721b2fc195eSAndrew Gallatin } 2722b2fc195eSAndrew Gallatin } 2723b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2724053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_low = 0xffffffff; 2725053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_high = 0xffffffff; 2726053e637fSAndrew Gallatin } 2727053e637fSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) { 2728b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 27296d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2730b2fc195eSAndrew Gallatin if (err) { 2731b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2732b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2733b2fc195eSAndrew Gallatin goto abort; 2734b2fc195eSAndrew Gallatin } 2735b2fc195eSAndrew Gallatin } 2736b2fc195eSAndrew Gallatin 2737b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2738b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2739b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2740c792928fSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 27415e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2742b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 27435e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2744b2fc195eSAndrew Gallatin &cmd); 2745053e637fSAndrew Gallatin cmd.data0 = big_bytes; 27465e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 27470fa7f681SAndrew Gallatin 27480fa7f681SAndrew Gallatin if (err != 0) { 27490fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 27500fa7f681SAndrew Gallatin goto abort; 27510fa7f681SAndrew Gallatin } 27520fa7f681SAndrew Gallatin 2753b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 27546d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 27556d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 27560fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 27570fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 27580fa7f681SAndrew Gallatin 27590fa7f681SAndrew Gallatin if (err != 0) { 27600fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 27610fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 27620fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 27630fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 27640fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 27650fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 27660fa7f681SAndrew Gallatin &cmd); 27670fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 27680fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 27690fa7f681SAndrew Gallatin } else { 27700fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 27710fa7f681SAndrew Gallatin } 2772b2fc195eSAndrew Gallatin 2773b2fc195eSAndrew Gallatin if (err != 0) { 2774b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2775b2fc195eSAndrew Gallatin goto abort; 2776b2fc195eSAndrew Gallatin } 2777b2fc195eSAndrew Gallatin 2778b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 27795e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2780b2fc195eSAndrew Gallatin if (err) { 2781b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2782b2fc195eSAndrew Gallatin goto abort; 2783b2fc195eSAndrew Gallatin } 2784b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2785b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2786b2fc195eSAndrew Gallatin 2787b2fc195eSAndrew Gallatin return 0; 2788b2fc195eSAndrew Gallatin 2789b2fc195eSAndrew Gallatin 2790b2fc195eSAndrew Gallatin abort: 27916d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2792a98d6cd7SAndrew Gallatin 2793b2fc195eSAndrew Gallatin return err; 2794b2fc195eSAndrew Gallatin } 2795b2fc195eSAndrew Gallatin 2796b2fc195eSAndrew Gallatin static int 27976d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2798b2fc195eSAndrew Gallatin { 2799053e637fSAndrew Gallatin struct lro_entry *lro_entry; 28006d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2801b2fc195eSAndrew Gallatin int err, old_down_cnt; 2802b2fc195eSAndrew Gallatin 2803b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2804b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2805b2fc195eSAndrew Gallatin mb(); 28065e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2807b2fc195eSAndrew Gallatin if (err) { 2808b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2809b2fc195eSAndrew Gallatin } 2810b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2811b2fc195eSAndrew Gallatin /* wait for down irq */ 2812dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 2813b2fc195eSAndrew Gallatin } 2814b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2815b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2816b2fc195eSAndrew Gallatin } 2817a98d6cd7SAndrew Gallatin 28186d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2819a98d6cd7SAndrew Gallatin 2820053e637fSAndrew Gallatin while (!SLIST_EMPTY(&sc->lro_free)) { 2821053e637fSAndrew Gallatin lro_entry = SLIST_FIRST(&sc->lro_free); 2822053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_free, next); 2823053e637fSAndrew Gallatin } 2824b2fc195eSAndrew Gallatin return 0; 2825b2fc195eSAndrew Gallatin } 2826b2fc195eSAndrew Gallatin 2827dce01b9bSAndrew Gallatin static void 2828dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 2829dce01b9bSAndrew Gallatin { 2830dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2831dce01b9bSAndrew Gallatin int reg; 2832dce01b9bSAndrew Gallatin uint16_t cmd, lnk, pectl; 2833dce01b9bSAndrew Gallatin 2834dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2835dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2836dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2837dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2838dce01b9bSAndrew Gallatin 2839dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2840dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2841dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 2842dce01b9bSAndrew Gallatin } 2843dce01b9bSAndrew Gallatin 2844dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 2845dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 2846dce01b9bSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2847dce01b9bSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2848dce01b9bSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2849dce01b9bSAndrew Gallatin } 2850dce01b9bSAndrew Gallatin 2851dce01b9bSAndrew Gallatin static uint32_t 2852dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 2853dce01b9bSAndrew Gallatin { 2854dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2855dce01b9bSAndrew Gallatin uint32_t vs; 2856dce01b9bSAndrew Gallatin 2857dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 2858dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { 2859dce01b9bSAndrew Gallatin device_printf(sc->dev, 2860dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 2861dce01b9bSAndrew Gallatin return (uint32_t)-1; 2862dce01b9bSAndrew Gallatin } 2863dce01b9bSAndrew Gallatin /* enable read32 mode */ 2864dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 2865dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 2866dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 2867dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 2868dce01b9bSAndrew Gallatin } 2869dce01b9bSAndrew Gallatin 2870dce01b9bSAndrew Gallatin static void 2871dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 2872dce01b9bSAndrew Gallatin { 2873dce01b9bSAndrew Gallatin int err; 2874dce01b9bSAndrew Gallatin uint32_t reboot; 2875dce01b9bSAndrew Gallatin uint16_t cmd; 2876dce01b9bSAndrew Gallatin 2877dce01b9bSAndrew Gallatin err = ENXIO; 2878dce01b9bSAndrew Gallatin 2879dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 2880dce01b9bSAndrew Gallatin 2881dce01b9bSAndrew Gallatin /* 2882dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 2883dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 2884dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 2885dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 2886dce01b9bSAndrew Gallatin * again 2887dce01b9bSAndrew Gallatin */ 2888dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2889dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2890dce01b9bSAndrew Gallatin /* 2891dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 2892dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 2893dce01b9bSAndrew Gallatin * back, then give up 2894dce01b9bSAndrew Gallatin */ 2895dce01b9bSAndrew Gallatin DELAY(1000*100); 2896dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2897dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2898dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 2899dce01b9bSAndrew Gallatin goto abort; 2900dce01b9bSAndrew Gallatin } 2901dce01b9bSAndrew Gallatin } 2902dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 2903dce01b9bSAndrew Gallatin /* print the reboot status */ 2904dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 2905dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 2906dce01b9bSAndrew Gallatin reboot); 2907dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 2908dce01b9bSAndrew Gallatin 2909dce01b9bSAndrew Gallatin /* XXXX waiting for pci_cfg_restore() to be exported */ 2910dce01b9bSAndrew Gallatin goto abort; /* just abort for now */ 2911dce01b9bSAndrew Gallatin 2912dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 2913dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2914dce01b9bSAndrew Gallatin } else { 2915dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC did not reboot, ring state:\n"); 2916dce01b9bSAndrew Gallatin device_printf(sc->dev, "tx.req=%d tx.done=%d\n", 2917dce01b9bSAndrew Gallatin sc->tx.req, sc->tx.done); 2918dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 2919dce01b9bSAndrew Gallatin sc->tx.pkt_done, 2920dce01b9bSAndrew Gallatin be32toh(sc->fw_stats->send_done_count)); 2921dce01b9bSAndrew Gallatin } 2922dce01b9bSAndrew Gallatin 2923dce01b9bSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { 2924dce01b9bSAndrew Gallatin mxge_close(sc); 2925dce01b9bSAndrew Gallatin err = mxge_open(sc); 2926dce01b9bSAndrew Gallatin } 2927dce01b9bSAndrew Gallatin 2928dce01b9bSAndrew Gallatin abort: 2929dce01b9bSAndrew Gallatin /* 2930dce01b9bSAndrew Gallatin * stop the watchdog if the nic is dead, to avoid spamming the 2931dce01b9bSAndrew Gallatin * console 2932dce01b9bSAndrew Gallatin */ 2933dce01b9bSAndrew Gallatin if (err != 0) { 2934dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2935dce01b9bSAndrew Gallatin } 2936dce01b9bSAndrew Gallatin } 2937dce01b9bSAndrew Gallatin 2938dce01b9bSAndrew Gallatin static void 2939dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 2940dce01b9bSAndrew Gallatin { 2941dce01b9bSAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 2942dce01b9bSAndrew Gallatin 2943dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 2944dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 2945dce01b9bSAndrew Gallatin if (tx->req != tx->done && 2946dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 2947dce01b9bSAndrew Gallatin tx->done == tx->watchdog_done) 2948dce01b9bSAndrew Gallatin mxge_watchdog_reset(sc); 2949dce01b9bSAndrew Gallatin 2950dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 2951dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 2952dce01b9bSAndrew Gallatin } 2953dce01b9bSAndrew Gallatin 2954dce01b9bSAndrew Gallatin static void 2955dce01b9bSAndrew Gallatin mxge_tick(void *arg) 2956dce01b9bSAndrew Gallatin { 2957dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 2958dce01b9bSAndrew Gallatin 2959dce01b9bSAndrew Gallatin 2960dce01b9bSAndrew Gallatin /* Synchronize with possible callout reset/stop. */ 2961dce01b9bSAndrew Gallatin if (callout_pending(&sc->co_hdl) || 2962dce01b9bSAndrew Gallatin !callout_active(&sc->co_hdl)) { 2963dce01b9bSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2964dce01b9bSAndrew Gallatin return; 2965dce01b9bSAndrew Gallatin } 2966dce01b9bSAndrew Gallatin 2967dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2968dce01b9bSAndrew Gallatin mxge_watchdog(sc); 2969dce01b9bSAndrew Gallatin } 2970b2fc195eSAndrew Gallatin 2971b2fc195eSAndrew Gallatin static int 29726d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2973b2fc195eSAndrew Gallatin { 2974b2fc195eSAndrew Gallatin return EINVAL; 2975b2fc195eSAndrew Gallatin } 2976b2fc195eSAndrew Gallatin 2977b2fc195eSAndrew Gallatin static int 29786d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2979b2fc195eSAndrew Gallatin { 2980b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2981b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2982b2fc195eSAndrew Gallatin int err = 0; 2983b2fc195eSAndrew Gallatin 2984b2fc195eSAndrew Gallatin 2985c792928fSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 2986053e637fSAndrew Gallatin if ((real_mtu > sc->max_mtu) || real_mtu < 60) 2987b2fc195eSAndrew Gallatin return EINVAL; 2988a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2989b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2990b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2991b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2992dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 29936d87a65dSAndrew Gallatin mxge_close(sc); 29946d87a65dSAndrew Gallatin err = mxge_open(sc); 2995b2fc195eSAndrew Gallatin if (err != 0) { 2996b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 29976d87a65dSAndrew Gallatin mxge_close(sc); 29986d87a65dSAndrew Gallatin (void) mxge_open(sc); 2999b2fc195eSAndrew Gallatin } 3000dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 3001b2fc195eSAndrew Gallatin } 3002a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3003b2fc195eSAndrew Gallatin return err; 3004b2fc195eSAndrew Gallatin } 3005b2fc195eSAndrew Gallatin 3006b2fc195eSAndrew Gallatin static void 30076d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 3008b2fc195eSAndrew Gallatin { 30096d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 3010b2fc195eSAndrew Gallatin 3011b2fc195eSAndrew Gallatin 3012b2fc195eSAndrew Gallatin if (sc == NULL) 3013b2fc195eSAndrew Gallatin return; 3014b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 3015b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 3016b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 3017b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 3018b2fc195eSAndrew Gallatin } 3019b2fc195eSAndrew Gallatin 3020b2fc195eSAndrew Gallatin static int 30216d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 3022b2fc195eSAndrew Gallatin { 30236d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 3024b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 3025b2fc195eSAndrew Gallatin int err, mask; 3026b2fc195eSAndrew Gallatin 3027b2fc195eSAndrew Gallatin err = 0; 3028b2fc195eSAndrew Gallatin switch (command) { 3029b2fc195eSAndrew Gallatin case SIOCSIFADDR: 3030b2fc195eSAndrew Gallatin case SIOCGIFADDR: 3031b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 3032b2fc195eSAndrew Gallatin break; 3033b2fc195eSAndrew Gallatin 3034b2fc195eSAndrew Gallatin case SIOCSIFMTU: 30356d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 3036b2fc195eSAndrew Gallatin break; 3037b2fc195eSAndrew Gallatin 3038b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 3039a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3040b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 3041dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 30426d87a65dSAndrew Gallatin err = mxge_open(sc); 3043dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, 3044dce01b9bSAndrew Gallatin mxge_tick, sc); 3045dce01b9bSAndrew Gallatin } else { 30460fa7f681SAndrew Gallatin /* take care of promis can allmulti 30470fa7f681SAndrew Gallatin flag chages */ 30480fa7f681SAndrew Gallatin mxge_change_promisc(sc, 30490fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 30500fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 30510fa7f681SAndrew Gallatin } 3052b2fc195eSAndrew Gallatin } else { 3053dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 30546d87a65dSAndrew Gallatin mxge_close(sc); 3055dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3056dce01b9bSAndrew Gallatin } 3057b2fc195eSAndrew Gallatin } 3058a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3059b2fc195eSAndrew Gallatin break; 3060b2fc195eSAndrew Gallatin 3061b2fc195eSAndrew Gallatin case SIOCADDMULTI: 3062b2fc195eSAndrew Gallatin case SIOCDELMULTI: 3063a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 30640fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 3065a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3066b2fc195eSAndrew Gallatin break; 3067b2fc195eSAndrew Gallatin 3068b2fc195eSAndrew Gallatin case SIOCSIFCAP: 3069a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3070b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 3071b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 3072b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 3073aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 3074aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 3075aed8e389SAndrew Gallatin | CSUM_TSO); 3076b2fc195eSAndrew Gallatin } else { 3077b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 3078b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 3079b2fc195eSAndrew Gallatin } 3080b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 3081b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 3082b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 30835e7d8541SAndrew Gallatin sc->csum_flag = 0; 3084b2fc195eSAndrew Gallatin } else { 3085b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 30865e7d8541SAndrew Gallatin sc->csum_flag = 1; 3087b2fc195eSAndrew Gallatin } 3088b2fc195eSAndrew Gallatin } 3089aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 3090aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 3091aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 3092aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 3093aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 3094aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 3095aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 3096aed8e389SAndrew Gallatin } else { 3097aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 3098aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 3099aed8e389SAndrew Gallatin err = EINVAL; 3100aed8e389SAndrew Gallatin } 3101aed8e389SAndrew Gallatin } 3102c792928fSAndrew Gallatin 3103c792928fSAndrew Gallatin if (mask & IFCAP_VLAN_HWTAGGING) 3104c792928fSAndrew Gallatin ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 3105a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3106c792928fSAndrew Gallatin VLAN_CAPABILITIES(ifp); 3107c792928fSAndrew Gallatin 3108b2fc195eSAndrew Gallatin break; 3109b2fc195eSAndrew Gallatin 3110b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 3111b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 3112b2fc195eSAndrew Gallatin &sc->media, command); 3113b2fc195eSAndrew Gallatin break; 3114b2fc195eSAndrew Gallatin 3115b2fc195eSAndrew Gallatin default: 3116b2fc195eSAndrew Gallatin err = ENOTTY; 3117b2fc195eSAndrew Gallatin } 3118b2fc195eSAndrew Gallatin return err; 3119b2fc195eSAndrew Gallatin } 3120b2fc195eSAndrew Gallatin 3121b2fc195eSAndrew Gallatin static void 31226d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 3123b2fc195eSAndrew Gallatin { 3124b2fc195eSAndrew Gallatin 31256d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 31266d87a65dSAndrew Gallatin &mxge_flow_control); 31276d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 31286d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 31296d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 31306d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 3131d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 3132d91b1b49SAndrew Gallatin &mxge_force_firmware); 31335e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 31345e7d8541SAndrew Gallatin &mxge_deassert_wait); 31355e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 31365e7d8541SAndrew Gallatin &mxge_verbose); 3137dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 3138053e637fSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt); 3139b2fc195eSAndrew Gallatin 31405e7d8541SAndrew Gallatin if (bootverbose) 31415e7d8541SAndrew Gallatin mxge_verbose = 1; 31426d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 31436d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 3144dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 3145dce01b9bSAndrew Gallatin mxge_ticks = hz; 31466d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 3147053e637fSAndrew Gallatin 3148b2fc195eSAndrew Gallatin } 3149b2fc195eSAndrew Gallatin 3150b2fc195eSAndrew Gallatin static int 31516d87a65dSAndrew Gallatin mxge_attach(device_t dev) 3152b2fc195eSAndrew Gallatin { 31536d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3154b2fc195eSAndrew Gallatin struct ifnet *ifp; 3155dce01b9bSAndrew Gallatin int count, rid, err; 3156b2fc195eSAndrew Gallatin 3157b2fc195eSAndrew Gallatin sc->dev = dev; 31586d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 3159b2fc195eSAndrew Gallatin 3160b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 3161b2fc195eSAndrew Gallatin 1, /* alignment */ 3162b2fc195eSAndrew Gallatin 4096, /* boundary */ 3163b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 3164b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 3165b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 3166aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 31675e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 3168b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 3169b2fc195eSAndrew Gallatin 0, /* flags */ 3170b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 3171b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 3172b2fc195eSAndrew Gallatin 3173b2fc195eSAndrew Gallatin if (err != 0) { 3174b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 3175b2fc195eSAndrew Gallatin err); 3176b2fc195eSAndrew Gallatin goto abort_with_nothing; 3177b2fc195eSAndrew Gallatin } 3178b2fc195eSAndrew Gallatin 3179b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 3180b2fc195eSAndrew Gallatin if (ifp == NULL) { 3181b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 3182b2fc195eSAndrew Gallatin err = ENOSPC; 3183b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 3184b2fc195eSAndrew Gallatin } 3185a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 3186a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3187a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 3188a98d6cd7SAndrew Gallatin snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", 3189a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3190a98d6cd7SAndrew Gallatin mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); 3191a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 3192a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 3193a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 3194b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 3195b2fc195eSAndrew Gallatin 3196dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 3197d91b1b49SAndrew Gallatin 3198dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 3199b2fc195eSAndrew Gallatin 3200b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 3201b2fc195eSAndrew Gallatin rid = PCIR_BARS; 3202b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 3203b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 3204b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 3205b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 3206b2fc195eSAndrew Gallatin err = ENXIO; 3207b2fc195eSAndrew Gallatin goto abort_with_lock; 3208b2fc195eSAndrew Gallatin } 3209b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 3210b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 3211b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 3212b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 3213b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 3214b2fc195eSAndrew Gallatin err = ENXIO; 3215b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3216b2fc195eSAndrew Gallatin } 3217b2fc195eSAndrew Gallatin 3218b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 3219b2fc195eSAndrew Gallatin lanai SRAM */ 32206d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 3221b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 3222b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 32236d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 3224b2fc195eSAndrew Gallatin sc->eeprom_strings, 32256d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 32266d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 3227b2fc195eSAndrew Gallatin if (err != 0) 3228b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3229b2fc195eSAndrew Gallatin 3230b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 32316d87a65dSAndrew Gallatin mxge_enable_wc(sc); 3232b2fc195eSAndrew Gallatin 3233b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 32346d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 32356d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 3236b2fc195eSAndrew Gallatin if (err != 0) 3237b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3238b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 32396d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 3240b2fc195eSAndrew Gallatin if (err != 0) 3241b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 3242b2fc195eSAndrew Gallatin 32436d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 3244b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 3245b2fc195eSAndrew Gallatin if (err != 0) 3246b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 32475e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 3248b2fc195eSAndrew Gallatin 3249a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 3250a98d6cd7SAndrew Gallatin if (err != 0) 3251a98d6cd7SAndrew Gallatin goto abort_with_fw_stats; 3252b2fc195eSAndrew Gallatin 3253b2fc195eSAndrew Gallatin /* Add our ithread */ 3254dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 3255dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 3256dc8731d4SAndrew Gallatin rid = 1; 3257dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 3258dc8731d4SAndrew Gallatin } else { 3259b2fc195eSAndrew Gallatin rid = 0; 3260dc8731d4SAndrew Gallatin } 3261b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 3262b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 3263b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 3264b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 3265adae7080SAndrew Gallatin goto abort_with_dmabench; 3266b2fc195eSAndrew Gallatin } 3267d91b1b49SAndrew Gallatin if (mxge_verbose) 3268dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 3269dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 3270dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 32718fe615baSAndrew Gallatin /* select & load the firmware */ 32728fe615baSAndrew Gallatin err = mxge_select_firmware(sc); 3273b2fc195eSAndrew Gallatin if (err != 0) 3274b2fc195eSAndrew Gallatin goto abort_with_irq_res; 32755e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 3276adae7080SAndrew Gallatin err = mxge_reset(sc, 0); 3277b2fc195eSAndrew Gallatin if (err != 0) 3278b2fc195eSAndrew Gallatin goto abort_with_irq_res; 3279b2fc195eSAndrew Gallatin 3280a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 3281a98d6cd7SAndrew Gallatin if (err != 0) { 3282a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 3283a98d6cd7SAndrew Gallatin goto abort_with_irq_res; 3284a98d6cd7SAndrew Gallatin } 3285a98d6cd7SAndrew Gallatin 3286a98d6cd7SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 3287a98d6cd7SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 3288ef544f63SPaolo Pisati NULL, mxge_intr, sc, &sc->ih); 3289a98d6cd7SAndrew Gallatin if (err != 0) { 3290a98d6cd7SAndrew Gallatin goto abort_with_rings; 3291a98d6cd7SAndrew Gallatin } 3292b2fc195eSAndrew Gallatin /* hook into the network stack */ 3293b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 3294b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 3295c792928fSAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | 3296c792928fSAndrew Gallatin IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; 3297c792928fSAndrew Gallatin 3298053e637fSAndrew Gallatin sc->max_mtu = mxge_max_mtu(sc); 3299053e637fSAndrew Gallatin if (sc->max_mtu >= 9000) 3300053e637fSAndrew Gallatin ifp->if_capabilities |= IFCAP_JUMBO_MTU; 3301053e637fSAndrew Gallatin else 3302053e637fSAndrew Gallatin device_printf(dev, "MTU limited to %d. Install " 3303adae7080SAndrew Gallatin "latest firmware for 9000 byte jumbo support\n", 3304053e637fSAndrew Gallatin sc->max_mtu - ETHER_HDR_LEN); 3305aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 3306b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 33075e7d8541SAndrew Gallatin sc->csum_flag = 1; 33086d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 3309b2fc195eSAndrew Gallatin ifp->if_softc = sc; 3310b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 33116d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 33126d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 3313b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 3314b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 3315053e637fSAndrew Gallatin if (ifp->if_capabilities & IFCAP_JUMBO_MTU) 3316c792928fSAndrew Gallatin ifp->if_mtu = 9000; 3317b2fc195eSAndrew Gallatin 3318b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 33196d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 33206d87a65dSAndrew Gallatin mxge_media_status); 3321b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 33226d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 3323b2fc195eSAndrew Gallatin return 0; 3324b2fc195eSAndrew Gallatin 3325a98d6cd7SAndrew Gallatin abort_with_rings: 3326a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3327b2fc195eSAndrew Gallatin abort_with_irq_res: 3328dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3329dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3330dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3331dc8731d4SAndrew Gallatin pci_release_msi(dev); 3332a98d6cd7SAndrew Gallatin abort_with_dmabench: 3333a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 33345e7d8541SAndrew Gallatin abort_with_fw_stats: 33356d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3336b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 33376d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 3338b2fc195eSAndrew Gallatin abort_with_cmd_dma: 33396d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3340b2fc195eSAndrew Gallatin abort_with_mem_res: 3341b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3342b2fc195eSAndrew Gallatin abort_with_lock: 3343b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3344a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3345a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3346a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3347b2fc195eSAndrew Gallatin if_free(ifp); 3348b2fc195eSAndrew Gallatin abort_with_parent_dmat: 3349b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3350b2fc195eSAndrew Gallatin 3351b2fc195eSAndrew Gallatin abort_with_nothing: 3352b2fc195eSAndrew Gallatin return err; 3353b2fc195eSAndrew Gallatin } 3354b2fc195eSAndrew Gallatin 3355b2fc195eSAndrew Gallatin static int 33566d87a65dSAndrew Gallatin mxge_detach(device_t dev) 3357b2fc195eSAndrew Gallatin { 33586d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3359b2fc195eSAndrew Gallatin 3360c792928fSAndrew Gallatin if (sc->ifp->if_vlantrunk != NULL) { 3361c792928fSAndrew Gallatin device_printf(sc->dev, 3362c792928fSAndrew Gallatin "Detach vlans before removing module\n"); 3363c792928fSAndrew Gallatin return EBUSY; 3364c792928fSAndrew Gallatin } 3365a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3366b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 33676d87a65dSAndrew Gallatin mxge_close(sc); 3368dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3369a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3370b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 3371dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 3372091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 3373a98d6cd7SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 3374a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3375dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3376dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3377dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3378dc8731d4SAndrew Gallatin pci_release_msi(dev); 3379dc8731d4SAndrew Gallatin 33805e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 33815e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 33826d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3383a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 33846d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 33856d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3386b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3387b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3388a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3389a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3390a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3391b2fc195eSAndrew Gallatin if_free(sc->ifp); 3392b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3393b2fc195eSAndrew Gallatin return 0; 3394b2fc195eSAndrew Gallatin } 3395b2fc195eSAndrew Gallatin 3396b2fc195eSAndrew Gallatin static int 33976d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 3398b2fc195eSAndrew Gallatin { 3399b2fc195eSAndrew Gallatin return 0; 3400b2fc195eSAndrew Gallatin } 3401b2fc195eSAndrew Gallatin 3402b2fc195eSAndrew Gallatin /* 3403b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 3404b2fc195eSAndrew Gallatin 3405b2fc195eSAndrew Gallatin Local Variables: 3406b2fc195eSAndrew Gallatin c-file-style:"linux" 3407b2fc195eSAndrew Gallatin tab-width:8 3408b2fc195eSAndrew Gallatin End: 3409b2fc195eSAndrew Gallatin */ 3410