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; 905e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024; 916d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 925e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 936d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 945e7d8541SAndrew Gallatin static int mxge_verbose = 0; 95dce01b9bSAndrew Gallatin static int mxge_ticks; 966d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 976d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 98b2fc195eSAndrew Gallatin 996d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 1006d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 1016d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 1026d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1036d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 104b2fc195eSAndrew Gallatin 1056d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 106b2fc195eSAndrew Gallatin { 107b2fc195eSAndrew Gallatin /* Device interface */ 1086d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1096d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1106d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1116d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 112b2fc195eSAndrew Gallatin {0, 0} 113b2fc195eSAndrew Gallatin }; 114b2fc195eSAndrew Gallatin 1156d87a65dSAndrew Gallatin static driver_t mxge_driver = 116b2fc195eSAndrew Gallatin { 1176d87a65dSAndrew Gallatin "mxge", 1186d87a65dSAndrew Gallatin mxge_methods, 1196d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 120b2fc195eSAndrew Gallatin }; 121b2fc195eSAndrew Gallatin 1226d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 123b2fc195eSAndrew Gallatin 124b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1256d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1266d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 127b2fc195eSAndrew Gallatin 1288fe615baSAndrew Gallatin static int mxge_load_firmware(mxge_softc_t *sc); 1298fe615baSAndrew Gallatin static int mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data); 1308fe615baSAndrew Gallatin 131b2fc195eSAndrew Gallatin static int 1326d87a65dSAndrew Gallatin mxge_probe(device_t dev) 133b2fc195eSAndrew Gallatin { 1346d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1356d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 136b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 137b2fc195eSAndrew Gallatin return 0; 138b2fc195eSAndrew Gallatin } 139b2fc195eSAndrew Gallatin return ENXIO; 140b2fc195eSAndrew Gallatin } 141b2fc195eSAndrew Gallatin 142b2fc195eSAndrew Gallatin static void 1436d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 144b2fc195eSAndrew Gallatin { 145b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 146b2fc195eSAndrew Gallatin vm_paddr_t pa; 147b2fc195eSAndrew Gallatin vm_offset_t len; 148b2fc195eSAndrew Gallatin int err, action; 149b2fc195eSAndrew Gallatin 150b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 151b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 152b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 153b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 154b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 155b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1566d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 157b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 158b2fc195eSAndrew Gallatin if (err != 0) { 159b2fc195eSAndrew Gallatin device_printf(sc->dev, 160b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 161b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 162b2fc195eSAndrew Gallatin } else { 163b2fc195eSAndrew Gallatin sc->wc = 1; 164b2fc195eSAndrew Gallatin } 165b2fc195eSAndrew Gallatin } 166b2fc195eSAndrew Gallatin 167b2fc195eSAndrew Gallatin 168b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 169b2fc195eSAndrew Gallatin static void 1706d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 171b2fc195eSAndrew Gallatin int error) 172b2fc195eSAndrew Gallatin { 173b2fc195eSAndrew Gallatin if (error == 0) { 174b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 175b2fc195eSAndrew Gallatin } 176b2fc195eSAndrew Gallatin } 177b2fc195eSAndrew Gallatin 178b2fc195eSAndrew Gallatin static int 1796d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 180b2fc195eSAndrew Gallatin bus_size_t alignment) 181b2fc195eSAndrew Gallatin { 182b2fc195eSAndrew Gallatin int err; 183b2fc195eSAndrew Gallatin device_t dev = sc->dev; 184b2fc195eSAndrew Gallatin 185b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 186b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 187b2fc195eSAndrew Gallatin alignment, /* alignment */ 188b2fc195eSAndrew Gallatin 4096, /* boundary */ 189b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 190b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 191b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 192b2fc195eSAndrew Gallatin bytes, /* maxsize */ 193b2fc195eSAndrew Gallatin 1, /* num segs */ 194b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 195b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 196b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 197b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 198b2fc195eSAndrew Gallatin if (err != 0) { 199b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 200b2fc195eSAndrew Gallatin return err; 201b2fc195eSAndrew Gallatin } 202b2fc195eSAndrew Gallatin 203b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 204b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 205b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 206b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 207b2fc195eSAndrew Gallatin if (err != 0) { 208b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 209b2fc195eSAndrew Gallatin goto abort_with_dmat; 210b2fc195eSAndrew Gallatin } 211b2fc195eSAndrew Gallatin 212b2fc195eSAndrew Gallatin /* load the memory */ 213b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2146d87a65dSAndrew Gallatin mxge_dmamap_callback, 215b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 216b2fc195eSAndrew Gallatin if (err != 0) { 217b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 218b2fc195eSAndrew Gallatin goto abort_with_mem; 219b2fc195eSAndrew Gallatin } 220b2fc195eSAndrew Gallatin return 0; 221b2fc195eSAndrew Gallatin 222b2fc195eSAndrew Gallatin abort_with_mem: 223b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 224b2fc195eSAndrew Gallatin abort_with_dmat: 225b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 226b2fc195eSAndrew Gallatin return err; 227b2fc195eSAndrew Gallatin } 228b2fc195eSAndrew Gallatin 229b2fc195eSAndrew Gallatin 230b2fc195eSAndrew Gallatin static void 2316d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 232b2fc195eSAndrew Gallatin { 233b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 234b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 235b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 236b2fc195eSAndrew Gallatin } 237b2fc195eSAndrew Gallatin 238b2fc195eSAndrew Gallatin /* 239b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 240b2fc195eSAndrew Gallatin * SN=x\0 241b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 242b2fc195eSAndrew Gallatin * PC=text\0 243b2fc195eSAndrew Gallatin */ 244b2fc195eSAndrew Gallatin 245b2fc195eSAndrew Gallatin static int 2466d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 247b2fc195eSAndrew Gallatin { 2486d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 249b2fc195eSAndrew Gallatin 250b2fc195eSAndrew Gallatin char *ptr, *limit; 251b2fc195eSAndrew Gallatin int i, found_mac; 252b2fc195eSAndrew Gallatin 253b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2546d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 255b2fc195eSAndrew Gallatin found_mac = 0; 256b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 257b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2585e7d8541SAndrew Gallatin ptr += 1; 259b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 260b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2615e7d8541SAndrew Gallatin ptr += 3; 262b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 263b2fc195eSAndrew Gallatin goto abort; 264b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 265b2fc195eSAndrew Gallatin found_mac = 1; 266b2fc195eSAndrew Gallatin } 2675e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2685e7d8541SAndrew Gallatin ptr += 3; 2695e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2705e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2715e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2725e7d8541SAndrew Gallatin ptr += 3; 2735e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2745e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 275b2fc195eSAndrew Gallatin } 2766d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 277b2fc195eSAndrew Gallatin } 278b2fc195eSAndrew Gallatin 279b2fc195eSAndrew Gallatin if (found_mac) 280b2fc195eSAndrew Gallatin return 0; 281b2fc195eSAndrew Gallatin 282b2fc195eSAndrew Gallatin abort: 283b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 284b2fc195eSAndrew Gallatin 285b2fc195eSAndrew Gallatin return ENXIO; 286b2fc195eSAndrew Gallatin } 287b2fc195eSAndrew Gallatin 288b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 2898fe615baSAndrew Gallatin static void 2908fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc) 291b2fc195eSAndrew Gallatin { 292b2fc195eSAndrew Gallatin uint32_t val; 2938fe615baSAndrew Gallatin unsigned long base, off; 294b2fc195eSAndrew Gallatin char *va, *cfgptr; 2958fe615baSAndrew Gallatin device_t pdev, mcp55; 2968fe615baSAndrew Gallatin uint16_t vendor_id, device_id, word; 297b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 298b2fc195eSAndrew Gallatin uint32_t *ptr32; 299b2fc195eSAndrew Gallatin 3008fe615baSAndrew Gallatin 3018fe615baSAndrew Gallatin if (!mxge_nvidia_ecrc_enable) 3028fe615baSAndrew Gallatin return; 3038fe615baSAndrew Gallatin 3048fe615baSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 3058fe615baSAndrew Gallatin if (pdev == NULL) { 3068fe615baSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 3078fe615baSAndrew Gallatin return; 3088fe615baSAndrew Gallatin } 3098fe615baSAndrew Gallatin vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2); 3108fe615baSAndrew Gallatin device_id = pci_read_config(pdev, PCIR_DEVICE, 2); 3118fe615baSAndrew Gallatin 3128fe615baSAndrew Gallatin if (vendor_id != 0x10de) 3138fe615baSAndrew Gallatin return; 3148fe615baSAndrew Gallatin 3158fe615baSAndrew Gallatin base = 0; 3168fe615baSAndrew Gallatin 3178fe615baSAndrew Gallatin if (device_id == 0x005d) { 3188fe615baSAndrew Gallatin /* ck804, base address is magic */ 3198fe615baSAndrew Gallatin base = 0xe0000000UL; 3208fe615baSAndrew Gallatin } else if (device_id >= 0x0374 && device_id <= 0x378) { 3218fe615baSAndrew Gallatin /* mcp55, base address stored in chipset */ 3228fe615baSAndrew Gallatin mcp55 = pci_find_bsf(0, 0, 0); 3238fe615baSAndrew Gallatin if (mcp55 && 3248fe615baSAndrew Gallatin 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) && 3258fe615baSAndrew Gallatin 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) { 3268fe615baSAndrew Gallatin word = pci_read_config(mcp55, 0x90, 2); 3278fe615baSAndrew Gallatin base = ((unsigned long)word & 0x7ffeU) << 25; 3288fe615baSAndrew Gallatin } 3298fe615baSAndrew Gallatin } 3308fe615baSAndrew Gallatin if (!base) 3318fe615baSAndrew Gallatin return; 3328fe615baSAndrew Gallatin 333b2fc195eSAndrew Gallatin /* XXXX 334b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 335b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 336b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 337b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 338b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 339b2fc195eSAndrew Gallatin */ 340b2fc195eSAndrew Gallatin #if 0 341b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 342b2fc195eSAndrew Gallatin config space */ 343b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 344b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 345b2fc195eSAndrew Gallatin val |= 0x40; 346b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 3478fe615baSAndrew Gallatin return; 348b2fc195eSAndrew Gallatin } 349b2fc195eSAndrew Gallatin #endif 350b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 351b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 352b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 353b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 354b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 355b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 356b2fc195eSAndrew Gallatin */ 357b2fc195eSAndrew Gallatin 358b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 359b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 360b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 361b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 362b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 363b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 364b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 365b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 366b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 367b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 368b2fc195eSAndrew Gallatin 3698fe615baSAndrew Gallatin off = base 370b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 371b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 372b2fc195eSAndrew Gallatin + 8 * slot); 373b2fc195eSAndrew Gallatin 374b2fc195eSAndrew Gallatin /* map it into the kernel */ 375b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 376b2fc195eSAndrew Gallatin 377b2fc195eSAndrew Gallatin 378b2fc195eSAndrew Gallatin if (va == NULL) { 379b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 3808fe615baSAndrew Gallatin return; 381b2fc195eSAndrew Gallatin } 382b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 383b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 384b2fc195eSAndrew Gallatin 385b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 386b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 387b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 388b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 389b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 390b2fc195eSAndrew Gallatin vendor_id, device_id); 391b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3928fe615baSAndrew Gallatin return; 393b2fc195eSAndrew Gallatin } 394b2fc195eSAndrew Gallatin 395b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 396b2fc195eSAndrew Gallatin val = *ptr32; 397b2fc195eSAndrew Gallatin 398b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 399b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 400b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4018fe615baSAndrew Gallatin return; 402b2fc195eSAndrew Gallatin } 403b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 404b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4055e7d8541SAndrew Gallatin if (mxge_verbose) 406b2fc195eSAndrew Gallatin device_printf(sc->dev, 4075e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 4085e7d8541SAndrew Gallatin "at %d:%d:%d\n", 409b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 4108fe615baSAndrew Gallatin return; 411b2fc195eSAndrew Gallatin } 412b2fc195eSAndrew Gallatin #else 4138fe615baSAndrew Gallatin static void 4146d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 415b2fc195eSAndrew Gallatin { 416b2fc195eSAndrew Gallatin device_printf(sc->dev, 417b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 4188fe615baSAndrew Gallatin return; 419b2fc195eSAndrew Gallatin } 420b2fc195eSAndrew Gallatin #endif 4218fe615baSAndrew Gallatin 4228fe615baSAndrew Gallatin 4238fe615baSAndrew Gallatin static int 4248fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type) 4258fe615baSAndrew Gallatin { 4268fe615baSAndrew Gallatin mxge_cmd_t cmd; 4278fe615baSAndrew Gallatin bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr; 4288fe615baSAndrew Gallatin int status; 4298fe615baSAndrew Gallatin uint32_t len; 4308fe615baSAndrew Gallatin char *test = " "; 4318fe615baSAndrew Gallatin 4328fe615baSAndrew Gallatin 4338fe615baSAndrew Gallatin /* Run a small DMA test. 4348fe615baSAndrew Gallatin * The magic multipliers to the length tell the firmware 4358fe615baSAndrew Gallatin * to do DMA read, write, or read+write tests. The 4368fe615baSAndrew Gallatin * results are returned in cmd.data0. The upper 16 4378fe615baSAndrew Gallatin * bits of the return is the number of transfers completed. 4388fe615baSAndrew Gallatin * The lower 16 bits is the time in 0.5us ticks that the 4398fe615baSAndrew Gallatin * transfers took to complete. 4408fe615baSAndrew Gallatin */ 4418fe615baSAndrew Gallatin 4428fe615baSAndrew Gallatin len = sc->tx.boundary; 4438fe615baSAndrew Gallatin 4448fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4458fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4468fe615baSAndrew Gallatin cmd.data2 = len * 0x10000; 4478fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4488fe615baSAndrew Gallatin if (status != 0) { 4498fe615baSAndrew Gallatin test = "read"; 4508fe615baSAndrew Gallatin goto abort; 4518fe615baSAndrew Gallatin } 4528fe615baSAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * len * 2) / 4538fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4548fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4558fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4568fe615baSAndrew Gallatin cmd.data2 = len * 0x1; 4578fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4588fe615baSAndrew Gallatin if (status != 0) { 4598fe615baSAndrew Gallatin test = "write"; 4608fe615baSAndrew Gallatin goto abort; 4618fe615baSAndrew Gallatin } 4628fe615baSAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * len * 2) / 4638fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4648fe615baSAndrew Gallatin 4658fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4668fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4678fe615baSAndrew Gallatin cmd.data2 = len * 0x10001; 4688fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4698fe615baSAndrew Gallatin if (status != 0) { 4708fe615baSAndrew Gallatin test = "read/write"; 4718fe615baSAndrew Gallatin goto abort; 4728fe615baSAndrew Gallatin } 4738fe615baSAndrew Gallatin sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) / 4748fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4758fe615baSAndrew Gallatin 4768fe615baSAndrew Gallatin abort: 4778fe615baSAndrew Gallatin if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) 4788fe615baSAndrew Gallatin device_printf(sc->dev, "DMA %s benchmark failed: %d\n", 4798fe615baSAndrew Gallatin test, status); 4808fe615baSAndrew Gallatin 4818fe615baSAndrew Gallatin return status; 4828fe615baSAndrew Gallatin } 4838fe615baSAndrew Gallatin 484b2fc195eSAndrew Gallatin /* 485b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 486b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 487b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 488b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 489b2fc195eSAndrew Gallatin * ECRC generation (if supported). 490b2fc195eSAndrew Gallatin * 491b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 492b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 493b2fc195eSAndrew Gallatin * 494b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 495b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 496b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 497b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 498b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 499b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 500b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 501b2fc195eSAndrew Gallatin */ 502b2fc195eSAndrew Gallatin 5038fe615baSAndrew Gallatin static int 5048fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc) 5058fe615baSAndrew Gallatin { 5068fe615baSAndrew Gallatin device_t dev = sc->dev; 5078fe615baSAndrew Gallatin int reg, status; 5088fe615baSAndrew Gallatin uint16_t pectl; 5098fe615baSAndrew Gallatin 5108fe615baSAndrew Gallatin sc->tx.boundary = 4096; 5118fe615baSAndrew Gallatin /* 5128fe615baSAndrew Gallatin * Verify the max read request size was set to 4KB 5138fe615baSAndrew Gallatin * before trying the test with 4KB. 5148fe615baSAndrew Gallatin */ 5158fe615baSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 5168fe615baSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 5178fe615baSAndrew Gallatin if ((pectl & (5 << 12)) != (5 << 12)) { 5188fe615baSAndrew Gallatin device_printf(dev, "Max Read Req. size != 4k (0x%x\n", 5198fe615baSAndrew Gallatin pectl); 5208fe615baSAndrew Gallatin sc->tx.boundary = 2048; 5218fe615baSAndrew Gallatin } 5228fe615baSAndrew Gallatin } 5238fe615baSAndrew Gallatin 5248fe615baSAndrew Gallatin /* 5258fe615baSAndrew Gallatin * load the optimized firmware (which assumes aligned PCIe 5268fe615baSAndrew Gallatin * completions) in order to see if it works on this host. 5278fe615baSAndrew Gallatin */ 5288fe615baSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 5298fe615baSAndrew Gallatin status = mxge_load_firmware(sc); 5308fe615baSAndrew Gallatin if (status != 0) { 5318fe615baSAndrew Gallatin return status; 5328fe615baSAndrew Gallatin } 5338fe615baSAndrew Gallatin 5348fe615baSAndrew Gallatin /* 5358fe615baSAndrew Gallatin * Enable ECRC if possible 5368fe615baSAndrew Gallatin */ 5378fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(sc); 5388fe615baSAndrew Gallatin 5398fe615baSAndrew Gallatin /* 5408fe615baSAndrew Gallatin * Run a DMA test which watches for unaligned completions and 5418fe615baSAndrew Gallatin * aborts on the first one seen. 5428fe615baSAndrew Gallatin */ 5438fe615baSAndrew Gallatin 5448fe615baSAndrew Gallatin status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST); 5458fe615baSAndrew Gallatin if (status == 0) 5468fe615baSAndrew Gallatin return 0; /* keep the aligned firmware */ 5478fe615baSAndrew Gallatin 5488fe615baSAndrew Gallatin if (status != E2BIG) 5498fe615baSAndrew Gallatin device_printf(dev, "DMA test failed: %d\n", status); 5508fe615baSAndrew Gallatin if (status == ENOSYS) 5518fe615baSAndrew Gallatin device_printf(dev, "Falling back to ethp! " 5528fe615baSAndrew Gallatin "Please install up to date fw\n"); 5538fe615baSAndrew Gallatin return status; 5548fe615baSAndrew Gallatin } 5558fe615baSAndrew Gallatin 5568fe615baSAndrew Gallatin static int 5576d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 558b2fc195eSAndrew Gallatin { 5598fe615baSAndrew Gallatin int aligned = 0; 560b2fc195eSAndrew Gallatin 561d91b1b49SAndrew Gallatin 562d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 563d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 564d91b1b49SAndrew Gallatin aligned = 1; 565d91b1b49SAndrew Gallatin else 566d91b1b49SAndrew Gallatin aligned = 0; 567d91b1b49SAndrew Gallatin if (mxge_verbose) 568d91b1b49SAndrew Gallatin device_printf(sc->dev, 569d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 570d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 571d91b1b49SAndrew Gallatin goto abort; 572d91b1b49SAndrew Gallatin } 573d91b1b49SAndrew Gallatin 574d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 575d91b1b49SAndrew Gallatin firmware and skip any checks */ 576d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 577d91b1b49SAndrew Gallatin device_printf(sc->dev, 578d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 579d91b1b49SAndrew Gallatin sc->link_width); 580d91b1b49SAndrew Gallatin aligned = 1; 581d91b1b49SAndrew Gallatin goto abort; 582d91b1b49SAndrew Gallatin } 583d91b1b49SAndrew Gallatin 5848fe615baSAndrew Gallatin if (0 == mxge_firmware_probe(sc)) 5858fe615baSAndrew Gallatin return 0; 586b2fc195eSAndrew Gallatin 587b2fc195eSAndrew Gallatin abort: 588b2fc195eSAndrew Gallatin if (aligned) { 5896d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 590b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 591b2fc195eSAndrew Gallatin } else { 5926d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 593b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 594b2fc195eSAndrew Gallatin } 5958fe615baSAndrew Gallatin return (mxge_load_firmware(sc)); 596b2fc195eSAndrew Gallatin } 597b2fc195eSAndrew Gallatin 598b2fc195eSAndrew Gallatin union qualhack 599b2fc195eSAndrew Gallatin { 600b2fc195eSAndrew Gallatin const char *ro_char; 601b2fc195eSAndrew Gallatin char *rw_char; 602b2fc195eSAndrew Gallatin }; 603b2fc195eSAndrew Gallatin 6044da0d523SAndrew Gallatin static int 6054da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 6064da0d523SAndrew Gallatin { 607b824b7d8SAndrew Gallatin 6084da0d523SAndrew Gallatin 6094da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 6104da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 6114da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 6124da0d523SAndrew Gallatin return EIO; 6134da0d523SAndrew Gallatin } 6144da0d523SAndrew Gallatin 6154da0d523SAndrew Gallatin /* save firmware version for sysctl */ 6164da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 6174da0d523SAndrew Gallatin if (mxge_verbose) 6184da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 6194da0d523SAndrew Gallatin 620b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 621b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 6224da0d523SAndrew Gallatin 623b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 624b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 6254da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 6264da0d523SAndrew Gallatin sc->fw_version); 6274da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 6284da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 6294da0d523SAndrew Gallatin return EINVAL; 6304da0d523SAndrew Gallatin } 6314da0d523SAndrew Gallatin return 0; 6324da0d523SAndrew Gallatin 6334da0d523SAndrew Gallatin } 634b2fc195eSAndrew Gallatin 635b2fc195eSAndrew Gallatin static int 6366d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 637b2fc195eSAndrew Gallatin { 63833d54970SLuigi Rizzo const struct firmware *fw; 639b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 640b2fc195eSAndrew Gallatin unsigned hdr_offset; 641b2fc195eSAndrew Gallatin const char *fw_data; 642b2fc195eSAndrew Gallatin union qualhack hack; 643b2fc195eSAndrew Gallatin int status; 6444da0d523SAndrew Gallatin unsigned int i; 6454da0d523SAndrew Gallatin char dummy; 646b2fc195eSAndrew Gallatin 647b2fc195eSAndrew Gallatin 648b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 649b2fc195eSAndrew Gallatin 650b2fc195eSAndrew Gallatin if (fw == NULL) { 651b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 652b2fc195eSAndrew Gallatin sc->fw_name); 653b2fc195eSAndrew Gallatin return ENOENT; 654b2fc195eSAndrew Gallatin } 655b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 656b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 657b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 658b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 659b2fc195eSAndrew Gallatin status = ENOSPC; 660b2fc195eSAndrew Gallatin goto abort_with_fw; 661b2fc195eSAndrew Gallatin } 662b2fc195eSAndrew Gallatin *limit = fw->datasize; 663b2fc195eSAndrew Gallatin 664b2fc195eSAndrew Gallatin /* check id */ 665b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 666b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 667b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 668b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 669b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 670b2fc195eSAndrew Gallatin status = EIO; 671b2fc195eSAndrew Gallatin goto abort_with_fw; 672b2fc195eSAndrew Gallatin } 673b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 674b2fc195eSAndrew Gallatin 6754da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 6764da0d523SAndrew Gallatin if (status != 0) 6774da0d523SAndrew Gallatin goto abort_with_fw; 678b2fc195eSAndrew Gallatin 679b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 680b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 6814da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 6824da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 6834da0d523SAndrew Gallatin hack.rw_char + i, 6844da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 6854da0d523SAndrew Gallatin mb(); 6864da0d523SAndrew Gallatin dummy = *sc->sram; 6874da0d523SAndrew Gallatin mb(); 6884da0d523SAndrew Gallatin } 689b2fc195eSAndrew Gallatin 690b2fc195eSAndrew Gallatin status = 0; 691b2fc195eSAndrew Gallatin abort_with_fw: 692b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 693b2fc195eSAndrew Gallatin return status; 694b2fc195eSAndrew Gallatin } 695b2fc195eSAndrew Gallatin 696b2fc195eSAndrew Gallatin /* 697b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 698b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 699b2fc195eSAndrew Gallatin */ 700b2fc195eSAndrew Gallatin 701b2fc195eSAndrew Gallatin static void 7026d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 703b2fc195eSAndrew Gallatin { 704b2fc195eSAndrew Gallatin char buf_bytes[72]; 705b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 706b2fc195eSAndrew Gallatin volatile char *submit; 707b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 708b2fc195eSAndrew Gallatin int i; 709b2fc195eSAndrew Gallatin 710b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 711b2fc195eSAndrew Gallatin 712b2fc195eSAndrew Gallatin /* clear confirmation addr */ 713b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 714b2fc195eSAndrew Gallatin *confirm = 0; 715b2fc195eSAndrew Gallatin mb(); 716b2fc195eSAndrew Gallatin 717b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 718b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 719b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 720b2fc195eSAndrew Gallatin */ 721b2fc195eSAndrew Gallatin 7226d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7236d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 724b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 725b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 726b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 7276d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 7286d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 729b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 730b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 731b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 732b2fc195eSAndrew Gallatin 733b2fc195eSAndrew Gallatin 7340fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 735b2fc195eSAndrew Gallatin 7366d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 737b2fc195eSAndrew Gallatin mb(); 738b2fc195eSAndrew Gallatin DELAY(1000); 739b2fc195eSAndrew Gallatin mb(); 740b2fc195eSAndrew Gallatin i = 0; 741b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 742b2fc195eSAndrew Gallatin DELAY(1000); 743b2fc195eSAndrew Gallatin i++; 744b2fc195eSAndrew Gallatin } 745b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 746b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 747b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 748b2fc195eSAndrew Gallatin *confirm); 749b2fc195eSAndrew Gallatin } 750b2fc195eSAndrew Gallatin return; 751b2fc195eSAndrew Gallatin } 752b2fc195eSAndrew Gallatin 753b2fc195eSAndrew Gallatin static int 7546d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 755b2fc195eSAndrew Gallatin { 756b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 757b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 758b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 7590fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 760b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 761e0501fd0SAndrew Gallatin int err, sleep_total = 0; 762b2fc195eSAndrew Gallatin 763b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 764b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 765b2fc195eSAndrew Gallatin 766b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 767b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 768b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 769b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 7706d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7716d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 772b2fc195eSAndrew Gallatin 773b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 774b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 775a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 776b2fc195eSAndrew Gallatin response->result = 0xffffffff; 777b2fc195eSAndrew Gallatin mb(); 7786d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 779b2fc195eSAndrew Gallatin 7805e7d8541SAndrew Gallatin /* wait up to 20ms */ 781e0501fd0SAndrew Gallatin err = EAGAIN; 7825e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 783b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 784b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 785b2fc195eSAndrew Gallatin mb(); 786e0501fd0SAndrew Gallatin switch (be32toh(response->result)) { 787e0501fd0SAndrew Gallatin case 0: 788b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 789e0501fd0SAndrew Gallatin err = 0; 790e0501fd0SAndrew Gallatin break; 791e0501fd0SAndrew Gallatin case 0xffffffff: 792e0501fd0SAndrew Gallatin DELAY(1000); 793e0501fd0SAndrew Gallatin break; 794e0501fd0SAndrew Gallatin case MXGEFW_CMD_UNKNOWN: 795e0501fd0SAndrew Gallatin err = ENOSYS; 796e0501fd0SAndrew Gallatin break; 797e0501fd0SAndrew Gallatin case MXGEFW_CMD_ERROR_UNALIGNED: 798e0501fd0SAndrew Gallatin err = E2BIG; 799e0501fd0SAndrew Gallatin break; 800e0501fd0SAndrew Gallatin default: 801b2fc195eSAndrew Gallatin device_printf(sc->dev, 8026d87a65dSAndrew Gallatin "mxge: command %d " 803b2fc195eSAndrew Gallatin "failed, result = %d\n", 804b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 805e0501fd0SAndrew Gallatin err = ENXIO; 806e0501fd0SAndrew Gallatin break; 807b2fc195eSAndrew Gallatin } 808e0501fd0SAndrew Gallatin if (err != EAGAIN) 809e0501fd0SAndrew Gallatin break; 810b2fc195eSAndrew Gallatin } 811e0501fd0SAndrew Gallatin if (err == EAGAIN) 8126d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 813b2fc195eSAndrew Gallatin "result = %d\n", 814b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 815e0501fd0SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 816e0501fd0SAndrew Gallatin return err; 817b2fc195eSAndrew Gallatin } 818b2fc195eSAndrew Gallatin 8194da0d523SAndrew Gallatin static int 8204da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 8214da0d523SAndrew Gallatin { 8224da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 8234da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 8244da0d523SAndrew Gallatin size_t hdr_offset; 8254da0d523SAndrew Gallatin int status; 8264da0d523SAndrew Gallatin 8274da0d523SAndrew Gallatin /* find running firmware header */ 8284da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 8294da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 8304da0d523SAndrew Gallatin 8314da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 8324da0d523SAndrew Gallatin device_printf(sc->dev, 8334da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 8344da0d523SAndrew Gallatin (int)hdr_offset); 8354da0d523SAndrew Gallatin return EIO; 8364da0d523SAndrew Gallatin } 8374da0d523SAndrew Gallatin 8384da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 8394da0d523SAndrew Gallatin * validate firmware */ 8404da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 8414da0d523SAndrew Gallatin if (hdr == NULL) { 8424da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 8434da0d523SAndrew Gallatin return ENOMEM; 8444da0d523SAndrew Gallatin } 8454da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 8464da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 8474da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 8484da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 8494da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 850b824b7d8SAndrew Gallatin 851b824b7d8SAndrew Gallatin /* 852b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 853b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 854b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 855b824b7d8SAndrew Gallatin */ 856b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 857b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 858b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 859b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 860b824b7d8SAndrew Gallatin "working around rx filter bug\n", 861b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 862b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 863b824b7d8SAndrew Gallatin } 864b824b7d8SAndrew Gallatin 8654da0d523SAndrew Gallatin return status; 8664da0d523SAndrew Gallatin } 8674da0d523SAndrew Gallatin 868b2fc195eSAndrew Gallatin 869b2fc195eSAndrew Gallatin static int 8706d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 871b2fc195eSAndrew Gallatin { 872b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 873b2fc195eSAndrew Gallatin volatile char *submit; 874b2fc195eSAndrew Gallatin char buf_bytes[72]; 875b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 876b2fc195eSAndrew Gallatin int status, i; 877b2fc195eSAndrew Gallatin 878b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 879b2fc195eSAndrew Gallatin 880b2fc195eSAndrew Gallatin size = sc->sram_size; 8816d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 882b2fc195eSAndrew Gallatin if (status) { 8834da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 8844da0d523SAndrew Gallatin it is new enough */ 8854da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 8864da0d523SAndrew Gallatin if (status) { 8874da0d523SAndrew Gallatin device_printf(sc->dev, 8884da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 889b2fc195eSAndrew Gallatin return status; 890b2fc195eSAndrew Gallatin } 8914da0d523SAndrew Gallatin device_printf(sc->dev, 8924da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 8934da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 8944da0d523SAndrew Gallatin device_printf(sc->dev, 8954da0d523SAndrew Gallatin "Using firmware currently running on NIC" 8964da0d523SAndrew Gallatin ". For optimal\n"); 8974da0d523SAndrew Gallatin device_printf(sc->dev, 8984da0d523SAndrew Gallatin "performance consider loading optimized " 8994da0d523SAndrew Gallatin "firmware\n"); 9004da0d523SAndrew Gallatin } 901d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 902d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 903d91b1b49SAndrew Gallatin return 0; 9044da0d523SAndrew Gallatin } 905b2fc195eSAndrew Gallatin /* clear confirmation addr */ 906b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 907b2fc195eSAndrew Gallatin *confirm = 0; 908b2fc195eSAndrew Gallatin mb(); 909b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 910b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 911b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 912b2fc195eSAndrew Gallatin */ 913b2fc195eSAndrew Gallatin 9146d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 9156d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 916b2fc195eSAndrew Gallatin 917b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 918b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 919b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 920b2fc195eSAndrew Gallatin 921b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 922b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 923b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 924b2fc195eSAndrew Gallatin */ 925b2fc195eSAndrew Gallatin /* where the code starts*/ 9266d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 927b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 928b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 929b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 930b2fc195eSAndrew Gallatin 9310fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 9326d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 933b2fc195eSAndrew Gallatin mb(); 934b2fc195eSAndrew Gallatin DELAY(1000); 935b2fc195eSAndrew Gallatin mb(); 936b2fc195eSAndrew Gallatin i = 0; 937b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 938b2fc195eSAndrew Gallatin DELAY(1000*10); 939b2fc195eSAndrew Gallatin i++; 940b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 941b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 942b2fc195eSAndrew Gallatin } 943b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 944b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 945b2fc195eSAndrew Gallatin confirm, *confirm); 946b2fc195eSAndrew Gallatin 947b2fc195eSAndrew Gallatin return ENXIO; 948b2fc195eSAndrew Gallatin } 949b2fc195eSAndrew Gallatin return 0; 950b2fc195eSAndrew Gallatin } 951b2fc195eSAndrew Gallatin 952b2fc195eSAndrew Gallatin static int 9536d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 954b2fc195eSAndrew Gallatin { 9556d87a65dSAndrew Gallatin mxge_cmd_t cmd; 956b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 957b2fc195eSAndrew Gallatin int status; 958b2fc195eSAndrew Gallatin 959b2fc195eSAndrew Gallatin 960b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 961b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 962b2fc195eSAndrew Gallatin 963b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 964b2fc195eSAndrew Gallatin 9655e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 966b2fc195eSAndrew Gallatin return status; 967b2fc195eSAndrew Gallatin } 968b2fc195eSAndrew Gallatin 969b2fc195eSAndrew Gallatin static int 9706d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 971b2fc195eSAndrew Gallatin { 9726d87a65dSAndrew Gallatin mxge_cmd_t cmd; 973b2fc195eSAndrew Gallatin int status; 974b2fc195eSAndrew Gallatin 975b2fc195eSAndrew Gallatin if (pause) 9765e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 977b2fc195eSAndrew Gallatin &cmd); 978b2fc195eSAndrew Gallatin else 9795e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 980b2fc195eSAndrew Gallatin &cmd); 981b2fc195eSAndrew Gallatin 982b2fc195eSAndrew Gallatin if (status) { 983b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 984b2fc195eSAndrew Gallatin return ENXIO; 985b2fc195eSAndrew Gallatin } 986b2fc195eSAndrew Gallatin sc->pause = pause; 987b2fc195eSAndrew Gallatin return 0; 988b2fc195eSAndrew Gallatin } 989b2fc195eSAndrew Gallatin 990b2fc195eSAndrew Gallatin static void 9916d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 992b2fc195eSAndrew Gallatin { 9936d87a65dSAndrew Gallatin mxge_cmd_t cmd; 994b2fc195eSAndrew Gallatin int status; 995b2fc195eSAndrew Gallatin 996b2fc195eSAndrew Gallatin if (promisc) 9975e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 998b2fc195eSAndrew Gallatin &cmd); 999b2fc195eSAndrew Gallatin else 10005e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 1001b2fc195eSAndrew Gallatin &cmd); 1002b2fc195eSAndrew Gallatin 1003b2fc195eSAndrew Gallatin if (status) { 1004b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 1005b2fc195eSAndrew Gallatin } 1006b2fc195eSAndrew Gallatin } 1007b2fc195eSAndrew Gallatin 10080fa7f681SAndrew Gallatin static void 10090fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 10100fa7f681SAndrew Gallatin { 10110fa7f681SAndrew Gallatin mxge_cmd_t cmd; 10120fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 10130fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 10140fa7f681SAndrew Gallatin int err; 10150fa7f681SAndrew Gallatin 10160fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 10170fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 10180fa7f681SAndrew Gallatin return; 10190fa7f681SAndrew Gallatin 10200fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 10210fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 10220fa7f681SAndrew Gallatin if (err != 0) { 10230fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 10240fa7f681SAndrew Gallatin " error status: %d\n", err); 10250fa7f681SAndrew Gallatin return; 10260fa7f681SAndrew Gallatin } 10270fa7f681SAndrew Gallatin 1028b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 1029b824b7d8SAndrew Gallatin return; 10300fa7f681SAndrew Gallatin 10310fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 10320fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 10330fa7f681SAndrew Gallatin return; 10340fa7f681SAndrew Gallatin 10350fa7f681SAndrew Gallatin /* Flush all the filters */ 10360fa7f681SAndrew Gallatin 10370fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 10380fa7f681SAndrew Gallatin if (err != 0) { 10390fa7f681SAndrew Gallatin device_printf(sc->dev, 10400fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 10410fa7f681SAndrew Gallatin ", error status: %d\n", err); 10420fa7f681SAndrew Gallatin return; 10430fa7f681SAndrew Gallatin } 10440fa7f681SAndrew Gallatin 10450fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 10460fa7f681SAndrew Gallatin 10470fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 10480fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 10490fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 10500fa7f681SAndrew Gallatin continue; 10510fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 10520fa7f681SAndrew Gallatin &cmd.data0, 4); 10530fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 10540fa7f681SAndrew Gallatin &cmd.data1, 2); 10550fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 10560fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 10570fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 10580fa7f681SAndrew Gallatin if (err != 0) { 10590fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 10600fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 10610fa7f681SAndrew Gallatin "%d\t", err); 10620fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 10630fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10640fa7f681SAndrew Gallatin return; 10650fa7f681SAndrew Gallatin } 10660fa7f681SAndrew Gallatin } 10670fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10680fa7f681SAndrew Gallatin /* Enable multicast filtering */ 10690fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 10700fa7f681SAndrew Gallatin if (err != 0) { 10710fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 10720fa7f681SAndrew Gallatin ", error status: %d\n", err); 10730fa7f681SAndrew Gallatin } 10740fa7f681SAndrew Gallatin } 10750fa7f681SAndrew Gallatin 1076b2fc195eSAndrew Gallatin static int 1077053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc) 1078053e637fSAndrew Gallatin { 1079053e637fSAndrew Gallatin mxge_cmd_t cmd; 1080053e637fSAndrew Gallatin int status; 1081053e637fSAndrew Gallatin 1082053e637fSAndrew Gallatin if (MJUMPAGESIZE - MXGEFW_PAD > MXGE_MAX_ETHER_MTU) 1083053e637fSAndrew Gallatin return MXGE_MAX_ETHER_MTU - MXGEFW_PAD; 1084053e637fSAndrew Gallatin 1085053e637fSAndrew Gallatin /* try to set nbufs to see if it we can 1086053e637fSAndrew Gallatin use virtually contiguous jumbos */ 1087053e637fSAndrew Gallatin cmd.data0 = 0; 1088053e637fSAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 1089053e637fSAndrew Gallatin &cmd); 1090053e637fSAndrew Gallatin if (status == 0) 1091053e637fSAndrew Gallatin return MXGE_MAX_ETHER_MTU - MXGEFW_PAD; 1092053e637fSAndrew Gallatin 1093053e637fSAndrew Gallatin /* otherwise, we're limited to MJUMPAGESIZE */ 1094053e637fSAndrew Gallatin return MJUMPAGESIZE - MXGEFW_PAD; 1095053e637fSAndrew Gallatin } 1096053e637fSAndrew Gallatin 1097053e637fSAndrew Gallatin static int 10986d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc) 1099b2fc195eSAndrew Gallatin { 1100b2fc195eSAndrew Gallatin 11016d87a65dSAndrew Gallatin mxge_cmd_t cmd; 11025e7d8541SAndrew Gallatin size_t bytes; 11035e7d8541SAndrew Gallatin int status; 1104b2fc195eSAndrew Gallatin 1105b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 1106b2fc195eSAndrew Gallatin is alive */ 1107b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 11085e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 1109b2fc195eSAndrew Gallatin if (status != 0) { 1110b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 1111b2fc195eSAndrew Gallatin return ENXIO; 1112b2fc195eSAndrew Gallatin } 1113b2fc195eSAndrew Gallatin 1114091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 1115091feecdSAndrew Gallatin 1116b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 11175e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * 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); 1124b2fc195eSAndrew Gallatin 11256d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 11265e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 11275e7d8541SAndrew Gallatin 11285e7d8541SAndrew Gallatin 11295e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 11305e7d8541SAndrew Gallatin 11315e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 11325e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 11335e7d8541SAndrew Gallatin 11345e7d8541SAndrew Gallatin 11355e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 11366d87a65dSAndrew Gallatin &cmd); 11375e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 1138b2fc195eSAndrew Gallatin if (status != 0) { 1139b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 1140b2fc195eSAndrew Gallatin return status; 1141b2fc195eSAndrew Gallatin } 1142b2fc195eSAndrew Gallatin 11435e7d8541SAndrew Gallatin 11445e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 11455e7d8541SAndrew Gallatin 11465e7d8541SAndrew Gallatin 11475e7d8541SAndrew Gallatin /* run a DMA benchmark */ 11488fe615baSAndrew Gallatin (void) mxge_dma_test(sc, MXGEFW_DMA_TEST); 11495e7d8541SAndrew Gallatin 1150b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 11515e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 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; 1158a82c2581SAndrew Gallatin sc->tx.stall = 0; 1159b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1160b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1161b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 1162a98d6cd7SAndrew Gallatin sc->fw_stats->valid = 0; 1163a98d6cd7SAndrew Gallatin sc->fw_stats->send_done_count = 0; 1164053e637fSAndrew Gallatin sc->lro_bad_csum = 0; 1165053e637fSAndrew Gallatin sc->lro_queued = 0; 1166053e637fSAndrew Gallatin sc->lro_flushed = 0; 11676d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 11686d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 11696d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 11700fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1171b2fc195eSAndrew Gallatin return status; 1172b2fc195eSAndrew Gallatin } 1173b2fc195eSAndrew Gallatin 1174b2fc195eSAndrew Gallatin static int 11756d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1176b2fc195eSAndrew Gallatin { 11776d87a65dSAndrew Gallatin mxge_softc_t *sc; 1178b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1179b2fc195eSAndrew Gallatin int err; 1180b2fc195eSAndrew Gallatin 1181b2fc195eSAndrew Gallatin sc = arg1; 1182b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1183b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1184b2fc195eSAndrew Gallatin if (err != 0) { 1185b2fc195eSAndrew Gallatin return err; 1186b2fc195eSAndrew Gallatin } 1187b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1188b2fc195eSAndrew Gallatin return 0; 1189b2fc195eSAndrew Gallatin 1190b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1191b2fc195eSAndrew Gallatin return EINVAL; 1192b2fc195eSAndrew Gallatin 1193a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 11945e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1195b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 11965e7d8541SAndrew Gallatin 1197a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1198b2fc195eSAndrew Gallatin return err; 1199b2fc195eSAndrew Gallatin } 1200b2fc195eSAndrew Gallatin 1201b2fc195eSAndrew Gallatin static int 12026d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1203b2fc195eSAndrew Gallatin { 12046d87a65dSAndrew Gallatin mxge_softc_t *sc; 1205b2fc195eSAndrew Gallatin unsigned int enabled; 1206b2fc195eSAndrew Gallatin int err; 1207b2fc195eSAndrew Gallatin 1208b2fc195eSAndrew Gallatin sc = arg1; 1209b2fc195eSAndrew Gallatin enabled = sc->pause; 1210b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1211b2fc195eSAndrew Gallatin if (err != 0) { 1212b2fc195eSAndrew Gallatin return err; 1213b2fc195eSAndrew Gallatin } 1214b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1215b2fc195eSAndrew Gallatin return 0; 1216b2fc195eSAndrew Gallatin 1217a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 12186d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1219a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1220b2fc195eSAndrew Gallatin return err; 1221b2fc195eSAndrew Gallatin } 1222b2fc195eSAndrew Gallatin 1223b2fc195eSAndrew Gallatin static int 12246d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1225b2fc195eSAndrew Gallatin { 1226b2fc195eSAndrew Gallatin int err; 1227b2fc195eSAndrew Gallatin 1228b2fc195eSAndrew Gallatin if (arg1 == NULL) 1229b2fc195eSAndrew Gallatin return EFAULT; 1230b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1231b2fc195eSAndrew Gallatin arg1 = NULL; 1232b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1233b2fc195eSAndrew Gallatin 1234b2fc195eSAndrew Gallatin return err; 1235b2fc195eSAndrew Gallatin } 1236b2fc195eSAndrew Gallatin 1237b2fc195eSAndrew Gallatin static void 12386d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1239b2fc195eSAndrew Gallatin { 1240b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1241b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 12425e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1243b2fc195eSAndrew Gallatin 1244b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1245b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1246b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1247b2fc195eSAndrew Gallatin 12485e7d8541SAndrew Gallatin /* random information */ 12495e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12505e7d8541SAndrew Gallatin "firmware_version", 12515e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 12525e7d8541SAndrew Gallatin 0, "firmware version"); 12535e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12545e7d8541SAndrew Gallatin "serial_number", 12555e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 12565e7d8541SAndrew Gallatin 0, "serial number"); 12575e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 12585e7d8541SAndrew Gallatin "product_code", 12595e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 12605e7d8541SAndrew Gallatin 0, "product_code"); 12615e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1262d91b1b49SAndrew Gallatin "pcie_link_width", 1263d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1264d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1265d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12665e7d8541SAndrew Gallatin "tx_boundary", 12675e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 12685e7d8541SAndrew Gallatin 0, "tx_boundary"); 12695e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1270091feecdSAndrew Gallatin "write_combine", 1271091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1272091feecdSAndrew Gallatin 0, "write combining PIO?"); 1273091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12745e7d8541SAndrew Gallatin "read_dma_MBs", 12755e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 12765e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 12775e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12785e7d8541SAndrew Gallatin "write_dma_MBs", 12795e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 12805e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 12815e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12825e7d8541SAndrew Gallatin "read_write_dma_MBs", 12835e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 12845e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 12855e7d8541SAndrew Gallatin 12865e7d8541SAndrew Gallatin 12875e7d8541SAndrew Gallatin /* performance related tunables */ 1288b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1289b2fc195eSAndrew Gallatin "intr_coal_delay", 1290b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 12916d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1292b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1293b2fc195eSAndrew Gallatin 1294b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1295b2fc195eSAndrew Gallatin "flow_control_enabled", 1296b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 12976d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1298b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1299b2fc195eSAndrew Gallatin 1300b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13015e7d8541SAndrew Gallatin "deassert_wait", 13025e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 13035e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1304b2fc195eSAndrew Gallatin 1305b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1306b2fc195eSAndrew Gallatin Need to swap it */ 1307b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1308b2fc195eSAndrew Gallatin "link_up", 1309b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 13106d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1311b2fc195eSAndrew Gallatin "I", "link up"); 1312b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1313b2fc195eSAndrew Gallatin "rdma_tags_available", 1314b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 13156d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1316b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1317b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1318b2fc195eSAndrew Gallatin "dropped_link_overflow", 1319b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 13206d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1321b2fc195eSAndrew Gallatin "I", "dropped_link_overflow"); 1322b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1323b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1324b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1325b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 13266d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1327b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1328b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 13290fa7f681SAndrew Gallatin "dropped_multicast_filtered", 13300fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 13310fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 13320fa7f681SAndrew Gallatin 0, mxge_handle_be32, 13330fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 13340fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1335b2fc195eSAndrew Gallatin "dropped_runt", 1336b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 13376d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1338b2fc195eSAndrew Gallatin "I", "dropped_runt"); 1339b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1340b2fc195eSAndrew Gallatin "dropped_overrun", 1341b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 13426d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1343b2fc195eSAndrew Gallatin "I", "dropped_overrun"); 1344b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1345b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1346b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1347b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 13486d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1349b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1350b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1351b2fc195eSAndrew Gallatin "dropped_no_big_buffer", 1352b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 13536d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1354b2fc195eSAndrew Gallatin "I", "dropped_no_big_buffer"); 1355b2fc195eSAndrew Gallatin 1356b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1357b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13585e7d8541SAndrew Gallatin "rx_small_cnt", 13595e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 13605e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 13615e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13625e7d8541SAndrew Gallatin "rx_big_cnt", 13635e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 13645e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 13655e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1366b2fc195eSAndrew Gallatin "tx_req", 1367b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1368b2fc195eSAndrew Gallatin 0, "tx_req"); 1369b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1370b2fc195eSAndrew Gallatin "tx_done", 1371b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1372b2fc195eSAndrew Gallatin 0, "tx_done"); 1373b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13745e7d8541SAndrew Gallatin "tx_pkt_done", 13755e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 13765e7d8541SAndrew Gallatin 0, "tx_done"); 1377a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1378a82c2581SAndrew Gallatin "tx_stall", 1379a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.stall, 1380a82c2581SAndrew Gallatin 0, "tx_stall"); 1381a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1382a82c2581SAndrew Gallatin "tx_wake", 1383a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.wake, 1384a82c2581SAndrew Gallatin 0, "tx_wake"); 13855e7d8541SAndrew Gallatin 13865e7d8541SAndrew Gallatin /* verbose printing? */ 1387b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13885e7d8541SAndrew Gallatin "verbose", 13895e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 13905e7d8541SAndrew Gallatin 0, "verbose printing"); 1391b2fc195eSAndrew Gallatin 1392053e637fSAndrew Gallatin /* lro */ 1393053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1394053e637fSAndrew Gallatin "lro_cnt", CTLFLAG_RW, &sc->lro_cnt, 1395053e637fSAndrew Gallatin 0, "number of lro merge queues"); 1396053e637fSAndrew Gallatin 1397053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1398053e637fSAndrew Gallatin "lro_flushed", CTLFLAG_RD, &sc->lro_flushed, 1399053e637fSAndrew Gallatin 0, "number of lro merge queues flushed"); 1400053e637fSAndrew Gallatin 1401053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1402053e637fSAndrew Gallatin "lro_queued", CTLFLAG_RD, &sc->lro_queued, 1403053e637fSAndrew Gallatin 0, "number of frames appended to lro merge queues"); 1404053e637fSAndrew Gallatin 1405b2fc195eSAndrew Gallatin } 1406b2fc195eSAndrew Gallatin 1407b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1408b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1409b2fc195eSAndrew Gallatin 1410b2fc195eSAndrew Gallatin static inline void 14116d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1412b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1413b2fc195eSAndrew Gallatin { 1414b2fc195eSAndrew Gallatin int idx, starting_slot; 1415b2fc195eSAndrew Gallatin starting_slot = tx->req; 1416b2fc195eSAndrew Gallatin while (cnt > 1) { 1417b2fc195eSAndrew Gallatin cnt--; 1418b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 14196d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1420b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1421b2fc195eSAndrew Gallatin mb(); 1422b2fc195eSAndrew Gallatin } 1423b2fc195eSAndrew Gallatin } 1424b2fc195eSAndrew Gallatin 1425b2fc195eSAndrew Gallatin /* 1426b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1427b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1428b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1429b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1430b2fc195eSAndrew Gallatin */ 1431b2fc195eSAndrew Gallatin 1432b2fc195eSAndrew Gallatin static inline void 14336d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1434b2fc195eSAndrew Gallatin int cnt) 1435b2fc195eSAndrew Gallatin { 1436b2fc195eSAndrew Gallatin int idx, i; 1437b2fc195eSAndrew Gallatin uint32_t *src_ints; 1438b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1439b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1440b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 14415e7d8541SAndrew Gallatin uint8_t last_flags; 1442b2fc195eSAndrew Gallatin 1443b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1444b2fc195eSAndrew Gallatin 14455e7d8541SAndrew Gallatin last_flags = src->flags; 14465e7d8541SAndrew Gallatin src->flags = 0; 1447b2fc195eSAndrew Gallatin mb(); 1448b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1449b2fc195eSAndrew Gallatin srcp = src; 1450b2fc195eSAndrew Gallatin 1451b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1452b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 14536d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1454b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1455b2fc195eSAndrew Gallatin srcp += 2; 1456b2fc195eSAndrew Gallatin dstp += 2; 1457b2fc195eSAndrew Gallatin } 1458b2fc195eSAndrew Gallatin } else { 1459b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1460b2fc195eSAndrew Gallatin that it is submitted below */ 14616d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1462b2fc195eSAndrew Gallatin i = 0; 1463b2fc195eSAndrew Gallatin } 1464b2fc195eSAndrew Gallatin if (i < cnt) { 1465b2fc195eSAndrew Gallatin /* submit the first request */ 14666d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1467b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1468b2fc195eSAndrew Gallatin } 1469b2fc195eSAndrew Gallatin 1470b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 14715e7d8541SAndrew Gallatin src->flags = last_flags; 1472b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1473b2fc195eSAndrew Gallatin src_ints+=3; 1474b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1475b2fc195eSAndrew Gallatin dst_ints+=3; 1476b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1477b2fc195eSAndrew Gallatin tx->req += cnt; 1478b2fc195eSAndrew Gallatin mb(); 1479b2fc195eSAndrew Gallatin } 1480b2fc195eSAndrew Gallatin 1481b2fc195eSAndrew Gallatin static inline void 14826d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt) 1483b2fc195eSAndrew Gallatin { 1484b2fc195eSAndrew Gallatin tx->req += cnt; 1485b2fc195eSAndrew Gallatin mb(); 1486b2fc195eSAndrew Gallatin while (cnt >= 4) { 14876d87a65dSAndrew Gallatin mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64); 1488b2fc195eSAndrew Gallatin mb(); 1489b2fc195eSAndrew Gallatin src += 4; 1490b2fc195eSAndrew Gallatin cnt -= 4; 1491b2fc195eSAndrew Gallatin } 1492b2fc195eSAndrew Gallatin if (cnt > 0) { 1493b2fc195eSAndrew Gallatin /* pad it to 64 bytes. The src is 64 bytes bigger than it 1494b2fc195eSAndrew Gallatin needs to be so that we don't overrun it */ 14950fa7f681SAndrew Gallatin mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64); 1496b2fc195eSAndrew Gallatin mb(); 1497b2fc195eSAndrew Gallatin } 1498b2fc195eSAndrew Gallatin } 1499b2fc195eSAndrew Gallatin 1500b2fc195eSAndrew Gallatin static void 1501aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt) 1502aed8e389SAndrew Gallatin { 1503aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1504aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1505aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1506aed8e389SAndrew Gallatin struct ether_header *eh; 1507aed8e389SAndrew Gallatin struct ip *ip; 1508aed8e389SAndrew Gallatin struct tcphdr *tcp; 1509aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1510aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1511aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1512aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1513aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1514aed8e389SAndrew Gallatin static int once; 1515aed8e389SAndrew Gallatin 1516aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1517aed8e389SAndrew Gallatin 1518aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1519aed8e389SAndrew Gallatin * send loop that we are still in the 1520aed8e389SAndrew Gallatin * header portion of the TSO packet. 1521aed8e389SAndrew Gallatin */ 1522aed8e389SAndrew Gallatin 1523aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1524aed8e389SAndrew Gallatin header together in the first mbuf, copy 1525aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1526aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1527aed8e389SAndrew Gallatin + sizeof (*ip))) { 1528aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1529aed8e389SAndrew Gallatin sc->scratch); 1530aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1531aed8e389SAndrew Gallatin } else { 1532aed8e389SAndrew Gallatin eh = mtod(m, struct ether_header *); 1533aed8e389SAndrew Gallatin } 1534aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1535aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2) 1536aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1537aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2) 1538aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1539aed8e389SAndrew Gallatin eh = (struct ether_header *) sc->scratch; 1540aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1541aed8e389SAndrew Gallatin } 1542aed8e389SAndrew Gallatin 1543aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1544aed8e389SAndrew Gallatin cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2)); 1545aed8e389SAndrew Gallatin 1546aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1547aed8e389SAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1548aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1549aed8e389SAndrew Gallatin 1550aed8e389SAndrew Gallatin 1551aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1552aed8e389SAndrew Gallatin * The firmware figures out where to put 1553aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1554aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1555aed8e389SAndrew Gallatin 1556aed8e389SAndrew Gallatin tx = &sc->tx; 1557aed8e389SAndrew Gallatin req = tx->req_list; 1558aed8e389SAndrew Gallatin seg = tx->seg_list; 1559aed8e389SAndrew Gallatin cnt = 0; 1560aed8e389SAndrew Gallatin rdma_count = 0; 1561aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1562aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1563aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1564aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1565aed8e389SAndrew Gallatin * to 0 after a segment cut. 1566aed8e389SAndrew Gallatin * 1567aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1568aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1569aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1570aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1571aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1572aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1573aed8e389SAndrew Gallatin * 1574aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1575aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1576aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1577aed8e389SAndrew Gallatin */ 1578aed8e389SAndrew Gallatin 1579aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1580aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1581aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1582aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1583e39a0a37SAndrew Gallatin len = seg->ds_len; 1584aed8e389SAndrew Gallatin 1585aed8e389SAndrew Gallatin while (len) { 1586aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1587e39a0a37SAndrew Gallatin seglen = len; 1588aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1589aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1590aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1591aed8e389SAndrew Gallatin /* payload */ 1592aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1593aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1594aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1595aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1596aed8e389SAndrew Gallatin flags_next |= next_is_first * 1597aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1598aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1599aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1600aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1601aed8e389SAndrew Gallatin /* header ends */ 1602aed8e389SAndrew Gallatin rdma_count = -1; 1603aed8e389SAndrew Gallatin cum_len_next = 0; 1604aed8e389SAndrew Gallatin seglen = -cum_len; 1605aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1606aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1607aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1608aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1609aed8e389SAndrew Gallatin } 1610aed8e389SAndrew Gallatin 1611aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1612aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1613aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1614aed8e389SAndrew Gallatin req->pad = 0; 1615aed8e389SAndrew Gallatin req->rdma_count = 1; 1616aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1617aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1618aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1619aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1620aed8e389SAndrew Gallatin low += seglen; 1621aed8e389SAndrew Gallatin len -= seglen; 1622aed8e389SAndrew Gallatin cum_len = cum_len_next; 1623aed8e389SAndrew Gallatin flags = flags_next; 1624aed8e389SAndrew Gallatin req++; 1625aed8e389SAndrew Gallatin cnt++; 1626aed8e389SAndrew Gallatin rdma_count++; 1627aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1628aed8e389SAndrew Gallatin cksum_offset -= seglen; 1629aed8e389SAndrew Gallatin else 1630aed8e389SAndrew Gallatin cksum_offset = 0; 1631aed8e389SAndrew Gallatin if (__predict_false(cnt > MXGE_MAX_SEND_DESC)) 1632aed8e389SAndrew Gallatin goto drop; 1633aed8e389SAndrew Gallatin } 1634aed8e389SAndrew Gallatin busdma_seg_cnt--; 1635aed8e389SAndrew Gallatin seg++; 1636aed8e389SAndrew Gallatin } 1637aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1638aed8e389SAndrew Gallatin 1639aed8e389SAndrew Gallatin do { 1640aed8e389SAndrew Gallatin req--; 1641aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1642aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1643aed8e389SAndrew Gallatin 1644aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1645aed8e389SAndrew Gallatin if (tx->wc_fifo == NULL) 1646aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1647aed8e389SAndrew Gallatin else 1648aed8e389SAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1649aed8e389SAndrew Gallatin return; 1650aed8e389SAndrew Gallatin 1651aed8e389SAndrew Gallatin drop: 1652e39a0a37SAndrew Gallatin bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map); 1653aed8e389SAndrew Gallatin m_freem(m); 1654aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1655aed8e389SAndrew Gallatin if (!once) { 1656aed8e389SAndrew Gallatin printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n"); 1657aed8e389SAndrew Gallatin printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list); 1658aed8e389SAndrew Gallatin once = 1; 1659aed8e389SAndrew Gallatin } 1660aed8e389SAndrew Gallatin return; 1661aed8e389SAndrew Gallatin 1662aed8e389SAndrew Gallatin } 1663aed8e389SAndrew Gallatin 1664aed8e389SAndrew Gallatin static void 16656d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1666b2fc195eSAndrew Gallatin { 1667b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1668b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1669b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1670b2fc195eSAndrew Gallatin struct ifnet *ifp; 16716d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1672b2fc195eSAndrew Gallatin struct ether_header *eh; 1673b2fc195eSAndrew Gallatin struct ip *ip; 1674aed8e389SAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag; 1675aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1676aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1677b2fc195eSAndrew Gallatin 1678b2fc195eSAndrew Gallatin 1679b2fc195eSAndrew Gallatin 1680b2fc195eSAndrew Gallatin ifp = sc->ifp; 1681b2fc195eSAndrew Gallatin tx = &sc->tx; 1682b2fc195eSAndrew Gallatin 1683b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1684b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1685b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1686aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1687b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1688b2fc195eSAndrew Gallatin if (err == EFBIG) { 1689b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1690b2fc195eSAndrew Gallatin to defrag */ 1691b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1692b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1693b2fc195eSAndrew Gallatin goto drop; 1694b2fc195eSAndrew Gallatin } 1695b2fc195eSAndrew Gallatin m = m_tmp; 1696b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1697b2fc195eSAndrew Gallatin tx->info[idx].map, 1698aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1699b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1700b2fc195eSAndrew Gallatin } 1701b2fc195eSAndrew Gallatin if (err != 0) { 1702aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1703aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1704b2fc195eSAndrew Gallatin goto drop; 1705b2fc195eSAndrew Gallatin } 1706b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1707b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 17085e7d8541SAndrew Gallatin tx->info[idx].m = m; 1709b2fc195eSAndrew Gallatin 1710aed8e389SAndrew Gallatin 1711aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1712aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1713aed8e389SAndrew Gallatin mxge_encap_tso(sc, m, cnt); 1714aed8e389SAndrew Gallatin return; 1715aed8e389SAndrew Gallatin } 1716aed8e389SAndrew Gallatin 1717b2fc195eSAndrew Gallatin req = tx->req_list; 1718b2fc195eSAndrew Gallatin cksum_offset = 0; 17195e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 17205e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1721b2fc195eSAndrew Gallatin 1722b2fc195eSAndrew Gallatin /* checksum offloading? */ 1723b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1724aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1725aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1726aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1727aed8e389SAndrew Gallatin + sizeof (*ip))) { 1728aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1729aed8e389SAndrew Gallatin sc->scratch); 1730aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1731aed8e389SAndrew Gallatin } else { 1732b2fc195eSAndrew Gallatin eh = mtod(m, struct ether_header *); 1733aed8e389SAndrew Gallatin } 1734b2fc195eSAndrew Gallatin ip = (struct ip *) (eh + 1); 1735b2fc195eSAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1736b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 17375e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1738b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 17395e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1740aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1741aed8e389SAndrew Gallatin } else { 1742aed8e389SAndrew Gallatin odd_flag = 0; 1743b2fc195eSAndrew Gallatin } 17445e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 17455e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1746b2fc195eSAndrew Gallatin 1747b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1748b2fc195eSAndrew Gallatin cum_len = 0; 1749aed8e389SAndrew Gallatin seg = tx->seg_list; 17505e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1751b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1752b2fc195eSAndrew Gallatin req->addr_low = 17536d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1754b2fc195eSAndrew Gallatin req->addr_high = 17556d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1756b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1757b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1758b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1759b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1760b2fc195eSAndrew Gallatin else 1761b2fc195eSAndrew Gallatin cksum_offset = 0; 17625e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 17635e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 17645e7d8541SAndrew Gallatin req->rdma_count = 1; 1765aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1766b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1767b2fc195eSAndrew Gallatin seg++; 1768b2fc195eSAndrew Gallatin req++; 1769b2fc195eSAndrew Gallatin req->flags = 0; 1770b2fc195eSAndrew Gallatin } 1771b2fc195eSAndrew Gallatin req--; 1772b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1773b2fc195eSAndrew Gallatin if (cum_len < 60) { 1774b2fc195eSAndrew Gallatin req++; 1775b2fc195eSAndrew Gallatin req->addr_low = 17766d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1777b2fc195eSAndrew Gallatin req->addr_high = 17786d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1779b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 17805e7d8541SAndrew Gallatin req->cksum_offset = 0; 17815e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 17825e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 17835e7d8541SAndrew Gallatin req->rdma_count = 1; 1784aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1785b2fc195eSAndrew Gallatin cnt++; 1786b2fc195eSAndrew Gallatin } 17875e7d8541SAndrew Gallatin 17885e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 17895e7d8541SAndrew Gallatin #if 0 17905e7d8541SAndrew Gallatin /* print what the firmware will see */ 17915e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 17925e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 17935e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 17945e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 17955e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 17965e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 17975e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 17985e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 17995e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 18005e7d8541SAndrew Gallatin } 18015e7d8541SAndrew Gallatin printf("--------------\n"); 18025e7d8541SAndrew Gallatin #endif 18035e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1804b2fc195eSAndrew Gallatin if (tx->wc_fifo == NULL) 18056d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1806b2fc195eSAndrew Gallatin else 18076d87a65dSAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1808b2fc195eSAndrew Gallatin return; 1809b2fc195eSAndrew Gallatin 1810b2fc195eSAndrew Gallatin drop: 1811b2fc195eSAndrew Gallatin m_freem(m); 1812b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1813b2fc195eSAndrew Gallatin return; 1814b2fc195eSAndrew Gallatin } 1815b2fc195eSAndrew Gallatin 1816b2fc195eSAndrew Gallatin 18176d914a32SAndrew Gallatin 18186d914a32SAndrew Gallatin 18196d914a32SAndrew Gallatin static inline void 18206d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1821b2fc195eSAndrew Gallatin { 1822b2fc195eSAndrew Gallatin struct mbuf *m; 1823b2fc195eSAndrew Gallatin struct ifnet *ifp; 1824b2fc195eSAndrew Gallatin 1825b2fc195eSAndrew Gallatin ifp = sc->ifp; 18266d914a32SAndrew Gallatin while ((sc->tx.mask - (sc->tx.req - sc->tx.done)) 18276d914a32SAndrew Gallatin > MXGE_MAX_SEND_DESC) { 1828b2fc195eSAndrew Gallatin 18296d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 18306d914a32SAndrew Gallatin if (m == NULL) { 18316d914a32SAndrew Gallatin return; 18326d914a32SAndrew Gallatin } 1833b2fc195eSAndrew Gallatin /* let BPF see it */ 1834b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1835b2fc195eSAndrew Gallatin 1836b2fc195eSAndrew Gallatin /* give it to the nic */ 18376d87a65dSAndrew Gallatin mxge_encap(sc, m); 18386d914a32SAndrew Gallatin } 18396d914a32SAndrew Gallatin /* ran out of transmit slots */ 1840a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 1841b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1842a82c2581SAndrew Gallatin sc->tx.stall++; 1843a82c2581SAndrew Gallatin } 1844b2fc195eSAndrew Gallatin } 1845b2fc195eSAndrew Gallatin 1846b2fc195eSAndrew Gallatin static void 18476d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1848b2fc195eSAndrew Gallatin { 18496d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1850b2fc195eSAndrew Gallatin 1851b2fc195eSAndrew Gallatin 1852a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 18536d87a65dSAndrew Gallatin mxge_start_locked(sc); 1854a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 1855b2fc195eSAndrew Gallatin } 1856b2fc195eSAndrew Gallatin 18575e7d8541SAndrew Gallatin /* 18585e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 18595e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 18605e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 18615e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 18625e7d8541SAndrew Gallatin * in a burst 18635e7d8541SAndrew Gallatin */ 18645e7d8541SAndrew Gallatin static inline void 18655e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 18665e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 18675e7d8541SAndrew Gallatin { 18685e7d8541SAndrew Gallatin uint32_t low; 18695e7d8541SAndrew Gallatin 18705e7d8541SAndrew Gallatin low = src->addr_low; 18715e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 1872a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 1873a1480dfbSAndrew Gallatin mb(); 1874a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 18755e7d8541SAndrew Gallatin mb(); 187640385a5fSAndrew Gallatin src->addr_low = low; 18775e7d8541SAndrew Gallatin dst->addr_low = low; 18785e7d8541SAndrew Gallatin mb(); 18795e7d8541SAndrew Gallatin } 18805e7d8541SAndrew Gallatin 1881b2fc195eSAndrew Gallatin static int 18826d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1883b2fc195eSAndrew Gallatin { 1884b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1885b2fc195eSAndrew Gallatin struct mbuf *m; 18866d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1887b2fc195eSAndrew Gallatin int cnt, err; 1888b2fc195eSAndrew Gallatin 1889b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1890b2fc195eSAndrew Gallatin if (m == NULL) { 1891b2fc195eSAndrew Gallatin rx->alloc_fail++; 1892b2fc195eSAndrew Gallatin err = ENOBUFS; 1893b2fc195eSAndrew Gallatin goto done; 1894b2fc195eSAndrew Gallatin } 1895b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1896b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1897b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1898b2fc195eSAndrew Gallatin if (err != 0) { 1899b2fc195eSAndrew Gallatin m_free(m); 1900b2fc195eSAndrew Gallatin goto done; 1901b2fc195eSAndrew Gallatin } 1902b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1903b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 19046d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1905b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 19066d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1907b2fc195eSAndrew Gallatin 1908b2fc195eSAndrew Gallatin done: 1909b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 19105e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 19115e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 19125e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 19135e7d8541SAndrew Gallatin else { 1914b2fc195eSAndrew Gallatin mb(); 19155e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 19165e7d8541SAndrew Gallatin } 1917b2fc195eSAndrew Gallatin } 1918b2fc195eSAndrew Gallatin return err; 1919b2fc195eSAndrew Gallatin } 1920b2fc195eSAndrew Gallatin 1921b2fc195eSAndrew Gallatin static int 19226d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1923b2fc195eSAndrew Gallatin { 1924053e637fSAndrew Gallatin bus_dma_segment_t seg[3]; 1925b2fc195eSAndrew Gallatin struct mbuf *m; 19266d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1927053e637fSAndrew Gallatin int cnt, err, i; 1928b2fc195eSAndrew Gallatin 1929053e637fSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size); 1930b2fc195eSAndrew Gallatin if (m == NULL) { 1931b2fc195eSAndrew Gallatin rx->alloc_fail++; 1932b2fc195eSAndrew Gallatin err = ENOBUFS; 1933b2fc195eSAndrew Gallatin goto done; 1934b2fc195eSAndrew Gallatin } 1935053e637fSAndrew Gallatin m->m_len = rx->cl_size; 1936b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1937053e637fSAndrew Gallatin seg, &cnt, BUS_DMA_NOWAIT); 1938b2fc195eSAndrew Gallatin if (err != 0) { 1939b2fc195eSAndrew Gallatin m_free(m); 1940b2fc195eSAndrew Gallatin goto done; 1941b2fc195eSAndrew Gallatin } 1942b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1943053e637fSAndrew Gallatin 1944053e637fSAndrew Gallatin for (i = 0; i < cnt; i++) { 1945053e637fSAndrew Gallatin rx->shadow[idx + i].addr_low = 1946053e637fSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr)); 1947053e637fSAndrew Gallatin rx->shadow[idx + i].addr_high = 1948053e637fSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr)); 1949053e637fSAndrew Gallatin } 1950053e637fSAndrew Gallatin 1951b2fc195eSAndrew Gallatin 1952b2fc195eSAndrew Gallatin done: 1953053e637fSAndrew Gallatin for (i = 0; i < rx->nbufs; i++) { 1954b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 19555e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 19565e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 19575e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 19585e7d8541SAndrew Gallatin else { 1959b2fc195eSAndrew Gallatin mb(); 19605e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 19615e7d8541SAndrew Gallatin } 1962b2fc195eSAndrew Gallatin } 1963053e637fSAndrew Gallatin idx++; 1964053e637fSAndrew Gallatin } 1965b2fc195eSAndrew Gallatin return err; 1966b2fc195eSAndrew Gallatin } 1967b2fc195eSAndrew Gallatin 19689b03b0f3SAndrew Gallatin /* 19699b03b0f3SAndrew Gallatin * Myri10GE hardware checksums are not valid if the sender 19709b03b0f3SAndrew Gallatin * padded the frame with non-zero padding. This is because 19719b03b0f3SAndrew Gallatin * the firmware just does a simple 16-bit 1s complement 19729b03b0f3SAndrew Gallatin * checksum across the entire frame, excluding the first 14 1973053e637fSAndrew Gallatin * bytes. It is best to simply to check the checksum and 1974053e637fSAndrew Gallatin * tell the stack about it only if the checksum is good 19759b03b0f3SAndrew Gallatin */ 19769b03b0f3SAndrew Gallatin 1977053e637fSAndrew Gallatin static inline uint16_t 1978053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 1979053e637fSAndrew Gallatin { 1980053e637fSAndrew Gallatin struct ether_header *eh; 1981053e637fSAndrew Gallatin struct ip *ip; 1982053e637fSAndrew Gallatin uint16_t c; 1983053e637fSAndrew Gallatin 1984053e637fSAndrew Gallatin eh = mtod(m, struct ether_header *); 1985053e637fSAndrew Gallatin 1986053e637fSAndrew Gallatin /* only deal with IPv4 TCP & UDP for now */ 1987053e637fSAndrew Gallatin if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP))) 1988053e637fSAndrew Gallatin return 1; 1989053e637fSAndrew Gallatin ip = (struct ip *)(eh + 1); 1990053e637fSAndrew Gallatin if (__predict_false(ip->ip_p != IPPROTO_TCP && 1991053e637fSAndrew Gallatin ip->ip_p != IPPROTO_UDP)) 1992053e637fSAndrew Gallatin return 1; 1993053e637fSAndrew Gallatin 1994053e637fSAndrew Gallatin c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 1995053e637fSAndrew Gallatin htonl(ntohs(csum) + ntohs(ip->ip_len) + 1996053e637fSAndrew Gallatin - (ip->ip_hl << 2) + ip->ip_p)); 1997053e637fSAndrew Gallatin c ^= 0xffff; 1998053e637fSAndrew Gallatin return (c); 19995e7d8541SAndrew Gallatin } 2000053e637fSAndrew Gallatin 20015e7d8541SAndrew Gallatin 20025e7d8541SAndrew Gallatin static inline void 2003053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2004b2fc195eSAndrew Gallatin { 2005b2fc195eSAndrew Gallatin struct ifnet *ifp; 2006053e637fSAndrew Gallatin struct mbuf *m; 20076d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2008053e637fSAndrew Gallatin bus_dmamap_t old_map; 2009b2fc195eSAndrew Gallatin int idx; 2010053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2011b2fc195eSAndrew Gallatin 2012b2fc195eSAndrew Gallatin ifp = sc->ifp; 2013053e637fSAndrew Gallatin rx = &sc->rx_big; 2014b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2015053e637fSAndrew Gallatin rx->cnt += rx->nbufs; 2016b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2017b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2018b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 20196d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 2020053e637fSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2021053e637fSAndrew Gallatin ifp->if_ierrors++; 2022053e637fSAndrew Gallatin return; 2023b2fc195eSAndrew Gallatin } 2024053e637fSAndrew Gallatin 2025b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2026b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2027b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2028b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2029b2fc195eSAndrew Gallatin 2030b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2031b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2032b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2033b2fc195eSAndrew Gallatin 2034053e637fSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2035053e637fSAndrew Gallatin * aligned */ 20365e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2037b2fc195eSAndrew Gallatin 2038053e637fSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 2039053e637fSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 20409b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2041b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2042053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2043053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2044b2fc195eSAndrew Gallatin return; 2045053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2046053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2047053e637fSAndrew Gallatin checksum is good */ 2048053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2049053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2050b2fc195eSAndrew Gallatin } 2051053e637fSAndrew Gallatin /* pass the frame up the stack */ 2052053e637fSAndrew Gallatin (*ifp->if_input)(ifp, m); 2053b2fc195eSAndrew Gallatin } 2054b2fc195eSAndrew Gallatin 2055b2fc195eSAndrew Gallatin static inline void 20565e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2057b2fc195eSAndrew Gallatin { 2058b2fc195eSAndrew Gallatin struct ifnet *ifp; 2059b2fc195eSAndrew Gallatin struct mbuf *m; 20606d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2061b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 2062b2fc195eSAndrew Gallatin int idx; 2063053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2064b2fc195eSAndrew Gallatin 2065b2fc195eSAndrew Gallatin ifp = sc->ifp; 2066b2fc195eSAndrew Gallatin rx = &sc->rx_small; 2067b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2068b2fc195eSAndrew Gallatin rx->cnt++; 2069b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2070b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2071b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 20726d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 2073b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2074b2fc195eSAndrew Gallatin ifp->if_ierrors++; 2075b2fc195eSAndrew Gallatin return; 2076b2fc195eSAndrew Gallatin } 2077b2fc195eSAndrew Gallatin 2078b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2079b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2080b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2081b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2082b2fc195eSAndrew Gallatin 2083b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2084b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2085b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2086b2fc195eSAndrew Gallatin 2087b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2088b2fc195eSAndrew Gallatin * aligned */ 20895e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2090b2fc195eSAndrew Gallatin 20919b03b0f3SAndrew Gallatin m->m_pkthdr.rcvif = ifp; 20929b03b0f3SAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 20939b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2094b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2095053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2096053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2097053e637fSAndrew Gallatin return; 2098053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2099053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2100053e637fSAndrew Gallatin checksum is good */ 2101053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2102053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2103053e637fSAndrew Gallatin } 2104b2fc195eSAndrew Gallatin 2105b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 2106b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 2107b2fc195eSAndrew Gallatin } 2108b2fc195eSAndrew Gallatin 2109b2fc195eSAndrew Gallatin static inline void 21105e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 21115e7d8541SAndrew Gallatin { 21125e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 2113053e637fSAndrew Gallatin struct lro_entry *lro; 21145e7d8541SAndrew Gallatin int limit = 0; 21155e7d8541SAndrew Gallatin uint16_t length; 21165e7d8541SAndrew Gallatin uint16_t checksum; 21175e7d8541SAndrew Gallatin 21185e7d8541SAndrew Gallatin 21195e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 21205e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 21215e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 2122053e637fSAndrew Gallatin checksum = rx_done->entry[rx_done->idx].checksum; 2123b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 21245e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 21255e7d8541SAndrew Gallatin else 21265e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 21275e7d8541SAndrew Gallatin rx_done->cnt++; 21285e7d8541SAndrew Gallatin rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1); 21295e7d8541SAndrew Gallatin 21305e7d8541SAndrew Gallatin /* limit potential for livelock */ 21315e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * mxge_max_intr_slots)) 21325e7d8541SAndrew Gallatin break; 2133053e637fSAndrew Gallatin } 2134053e637fSAndrew Gallatin while(!SLIST_EMPTY(&sc->lro_active)) { 2135053e637fSAndrew Gallatin lro = SLIST_FIRST(&sc->lro_active); 2136053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_active, next); 2137053e637fSAndrew Gallatin mxge_lro_flush(sc, lro); 21385e7d8541SAndrew Gallatin } 21395e7d8541SAndrew Gallatin } 21405e7d8541SAndrew Gallatin 21415e7d8541SAndrew Gallatin 21425e7d8541SAndrew Gallatin static inline void 21436d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 2144b2fc195eSAndrew Gallatin { 2145b2fc195eSAndrew Gallatin struct ifnet *ifp; 21466d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 2147b2fc195eSAndrew Gallatin struct mbuf *m; 2148b2fc195eSAndrew Gallatin bus_dmamap_t map; 21495e7d8541SAndrew Gallatin int idx, limit; 2150b2fc195eSAndrew Gallatin 21515e7d8541SAndrew Gallatin limit = 0; 2152b2fc195eSAndrew Gallatin tx = &sc->tx; 2153b2fc195eSAndrew Gallatin ifp = sc->ifp; 21545e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2155b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2156b2fc195eSAndrew Gallatin tx->done++; 2157b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2158b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2159b2fc195eSAndrew Gallatin segment per-mbuf */ 2160b2fc195eSAndrew Gallatin if (m != NULL) { 2161b2fc195eSAndrew Gallatin ifp->if_opackets++; 2162b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2163b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2164b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2165b2fc195eSAndrew Gallatin m_freem(m); 2166b2fc195eSAndrew Gallatin } 21675e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 21685e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 21695e7d8541SAndrew Gallatin tx->pkt_done++; 21705e7d8541SAndrew Gallatin } 21715e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 21725e7d8541SAndrew Gallatin 2 full tx rings per call */ 21735e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 21745e7d8541SAndrew Gallatin break; 2175b2fc195eSAndrew Gallatin } 2176b2fc195eSAndrew Gallatin 2177b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2178b2fc195eSAndrew Gallatin its OK to send packets */ 2179b2fc195eSAndrew Gallatin 2180b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2181b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2182a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 2183b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2184a82c2581SAndrew Gallatin sc->tx.wake++; 21856d87a65dSAndrew Gallatin mxge_start_locked(sc); 2186a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 2187b2fc195eSAndrew Gallatin } 2188b2fc195eSAndrew Gallatin } 2189b2fc195eSAndrew Gallatin 2190b2fc195eSAndrew Gallatin static void 21916d87a65dSAndrew Gallatin mxge_intr(void *arg) 2192b2fc195eSAndrew Gallatin { 21936d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 21945e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 21955e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 21965e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 21975e7d8541SAndrew Gallatin uint32_t send_done_count; 21985e7d8541SAndrew Gallatin uint8_t valid; 2199b2fc195eSAndrew Gallatin 2200b2fc195eSAndrew Gallatin 22015e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 22025e7d8541SAndrew Gallatin if (!stats->valid) { 22035e7d8541SAndrew Gallatin return; 2204b2fc195eSAndrew Gallatin } 22055e7d8541SAndrew Gallatin valid = stats->valid; 2206b2fc195eSAndrew Gallatin 2207dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 22085e7d8541SAndrew Gallatin /* lower legacy IRQ */ 22095e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 22105e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 22115e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 22125e7d8541SAndrew Gallatin stats->valid = 0; 2213dc8731d4SAndrew Gallatin } else { 2214dc8731d4SAndrew Gallatin stats->valid = 0; 2215dc8731d4SAndrew Gallatin } 2216dc8731d4SAndrew Gallatin 2217dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 22185e7d8541SAndrew Gallatin do { 22195e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 22205e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 22215e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 22225e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 22235e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 22245e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 22255e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2226b2fc195eSAndrew Gallatin } 22275e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2228b2fc195eSAndrew Gallatin 22295e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 22305e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 22315e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2232b2fc195eSAndrew Gallatin if (sc->link_state) { 22335e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 22345e7d8541SAndrew Gallatin if (mxge_verbose) 22355e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2236b2fc195eSAndrew Gallatin } else { 22375e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 22385e7d8541SAndrew Gallatin if (mxge_verbose) 22395e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2240b2fc195eSAndrew Gallatin } 2241b2fc195eSAndrew Gallatin } 2242b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2243b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2244b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2245b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 22465e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 22475e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 22485e7d8541SAndrew Gallatin } 22495e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2250b2fc195eSAndrew Gallatin } 2251b2fc195eSAndrew Gallatin 22525e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 22535e7d8541SAndrew Gallatin if (valid & 0x1) 22545e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 22555e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2256b2fc195eSAndrew Gallatin } 2257b2fc195eSAndrew Gallatin 2258b2fc195eSAndrew Gallatin static void 22596d87a65dSAndrew Gallatin mxge_init(void *arg) 2260b2fc195eSAndrew Gallatin { 2261b2fc195eSAndrew Gallatin } 2262b2fc195eSAndrew Gallatin 2263b2fc195eSAndrew Gallatin 2264b2fc195eSAndrew Gallatin 2265b2fc195eSAndrew Gallatin static void 22666d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2267b2fc195eSAndrew Gallatin { 2268b2fc195eSAndrew Gallatin int i; 2269b2fc195eSAndrew Gallatin 2270b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2271b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2272b2fc195eSAndrew Gallatin continue; 2273b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2274b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2275b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2276b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2277b2fc195eSAndrew Gallatin } 2278b2fc195eSAndrew Gallatin 22799b03b0f3SAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 22809b03b0f3SAndrew Gallatin if (sc->rx_small.info[i].m == NULL) 2281b2fc195eSAndrew Gallatin continue; 22829b03b0f3SAndrew Gallatin bus_dmamap_unload(sc->rx_small.dmat, 22839b03b0f3SAndrew Gallatin sc->rx_small.info[i].map); 22849b03b0f3SAndrew Gallatin m_freem(sc->rx_small.info[i].m); 22859b03b0f3SAndrew Gallatin sc->rx_small.info[i].m = NULL; 2286b2fc195eSAndrew Gallatin } 2287b2fc195eSAndrew Gallatin 2288b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2289dce01b9bSAndrew Gallatin sc->tx.info[i].flag = 0; 2290b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2291b2fc195eSAndrew Gallatin continue; 2292b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2293b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2294b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2295b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2296b2fc195eSAndrew Gallatin } 2297b2fc195eSAndrew Gallatin } 2298b2fc195eSAndrew Gallatin 2299b2fc195eSAndrew Gallatin static void 23006d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2301b2fc195eSAndrew Gallatin { 2302b2fc195eSAndrew Gallatin int i; 2303b2fc195eSAndrew Gallatin 2304aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2305b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2306aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2307aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2308b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2309b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2310b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2311b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2312b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2313c2657176SAndrew Gallatin if (sc->tx.dmat != NULL) { 2314b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2315b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2316b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2317b2fc195eSAndrew Gallatin } 2318c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2319c2657176SAndrew Gallatin } 2320b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2321b2fc195eSAndrew Gallatin } 2322b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2323c2657176SAndrew Gallatin if (sc->rx_small.dmat != NULL) { 2324b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2325b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2326b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2327b2fc195eSAndrew Gallatin } 2328c2657176SAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2329c2657176SAndrew Gallatin sc->rx_small.extra_map); 2330c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2331c2657176SAndrew Gallatin } 2332b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2333b2fc195eSAndrew Gallatin } 2334b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2335c2657176SAndrew Gallatin if (sc->rx_big.dmat != NULL) { 2336b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2337b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2338b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2339b2fc195eSAndrew Gallatin } 2340b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2341b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2342b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2343b2fc195eSAndrew Gallatin } 2344c2657176SAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2345c2657176SAndrew Gallatin } 2346c2657176SAndrew Gallatin } 2347b2fc195eSAndrew Gallatin 2348b2fc195eSAndrew Gallatin static int 23496d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2350b2fc195eSAndrew Gallatin { 23516d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2352b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2353b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2354b2fc195eSAndrew Gallatin int i, err; 2355b2fc195eSAndrew Gallatin unsigned long bytes; 2356b2fc195eSAndrew Gallatin 2357b2fc195eSAndrew Gallatin /* get ring sizes */ 23585e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2359b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 23605e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2361b2fc195eSAndrew Gallatin if (err != 0) { 2362b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2363b2fc195eSAndrew Gallatin goto abort_with_nothing; 2364b2fc195eSAndrew Gallatin } 2365b2fc195eSAndrew Gallatin 2366b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2367b2fc195eSAndrew Gallatin 2368b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2369b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 237076bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 2371a82c2581SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 237276bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2373b2fc195eSAndrew Gallatin 2374b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2375b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2376b2fc195eSAndrew Gallatin 2377b2fc195eSAndrew Gallatin err = ENOMEM; 2378b2fc195eSAndrew Gallatin 2379b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2380b2fc195eSAndrew Gallatin bytes = 8 + 23815e7d8541SAndrew Gallatin sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4); 2382b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2383b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2384b2fc195eSAndrew Gallatin goto abort_with_nothing; 2385b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2386b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2387b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2388b2fc195eSAndrew Gallatin 2389aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2390aed8e389SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC; 2391aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2392aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2393aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2394aed8e389SAndrew Gallatin goto abort_with_alloc; 2395aed8e389SAndrew Gallatin 2396b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2397b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2398b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2399b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2400b2fc195eSAndrew Gallatin goto abort_with_alloc; 2401b2fc195eSAndrew Gallatin 2402b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2403b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2404b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2405b2fc195eSAndrew Gallatin goto abort_with_alloc; 2406b2fc195eSAndrew Gallatin 2407b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2408b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2409b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2410b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2411b2fc195eSAndrew Gallatin goto abort_with_alloc; 2412b2fc195eSAndrew Gallatin 2413b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2414b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2415b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2416b2fc195eSAndrew Gallatin goto abort_with_alloc; 2417b2fc195eSAndrew Gallatin 2418b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2419b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2420b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2421b2fc195eSAndrew Gallatin goto abort_with_alloc; 2422b2fc195eSAndrew Gallatin 2423b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2424b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2425b2fc195eSAndrew Gallatin 1, /* alignment */ 2426b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2427b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2428b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2429b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2430aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2431aed8e389SAndrew Gallatin MXGE_MAX_SEND_DESC/2, /* num segs */ 2432b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2433b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2434b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2435b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2436b2fc195eSAndrew Gallatin 2437b2fc195eSAndrew Gallatin if (err != 0) { 2438b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2439b2fc195eSAndrew Gallatin err); 2440b2fc195eSAndrew Gallatin goto abort_with_alloc; 2441b2fc195eSAndrew Gallatin } 2442b2fc195eSAndrew Gallatin 2443b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2444b2fc195eSAndrew Gallatin 1, /* alignment */ 2445b2fc195eSAndrew Gallatin 4096, /* boundary */ 2446b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2447b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2448b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2449b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2450b2fc195eSAndrew Gallatin 1, /* num segs */ 2451b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2452b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2453b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2454b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2455b2fc195eSAndrew Gallatin if (err != 0) { 2456b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2457b2fc195eSAndrew Gallatin err); 2458b2fc195eSAndrew Gallatin goto abort_with_alloc; 2459b2fc195eSAndrew Gallatin } 2460b2fc195eSAndrew Gallatin 2461b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2462b2fc195eSAndrew Gallatin 1, /* alignment */ 2463b2fc195eSAndrew Gallatin 4096, /* boundary */ 2464b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2465b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2466b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2467053e637fSAndrew Gallatin 3*4096, /* maxsize */ 2468053e637fSAndrew Gallatin 3, /* num segs */ 2469b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2470b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2471b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2472b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2473b2fc195eSAndrew Gallatin if (err != 0) { 2474b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2475b2fc195eSAndrew Gallatin err); 2476b2fc195eSAndrew Gallatin goto abort_with_alloc; 2477b2fc195eSAndrew Gallatin } 2478b2fc195eSAndrew Gallatin 2479b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2480b2fc195eSAndrew Gallatin in each ring */ 2481b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2482b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2483b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2484b2fc195eSAndrew Gallatin if (err != 0) { 2485b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2486b2fc195eSAndrew Gallatin err); 2487b2fc195eSAndrew Gallatin goto abort_with_alloc; 2488b2fc195eSAndrew Gallatin } 2489b2fc195eSAndrew Gallatin } 2490b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2491b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2492b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2493b2fc195eSAndrew Gallatin if (err != 0) { 2494b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2495b2fc195eSAndrew Gallatin err); 2496b2fc195eSAndrew Gallatin goto abort_with_alloc; 2497b2fc195eSAndrew Gallatin } 2498b2fc195eSAndrew Gallatin } 2499b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2500b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2501b2fc195eSAndrew Gallatin if (err != 0) { 2502b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2503b2fc195eSAndrew Gallatin err); 2504b2fc195eSAndrew Gallatin goto abort_with_alloc; 2505b2fc195eSAndrew Gallatin } 2506b2fc195eSAndrew Gallatin 2507b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2508b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2509b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2510b2fc195eSAndrew Gallatin if (err != 0) { 2511b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2512b2fc195eSAndrew Gallatin err); 2513b2fc195eSAndrew Gallatin goto abort_with_alloc; 2514b2fc195eSAndrew Gallatin } 2515b2fc195eSAndrew Gallatin } 2516b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2517b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2518b2fc195eSAndrew Gallatin if (err != 0) { 2519b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2520b2fc195eSAndrew Gallatin err); 2521b2fc195eSAndrew Gallatin goto abort_with_alloc; 2522b2fc195eSAndrew Gallatin } 2523b2fc195eSAndrew Gallatin return 0; 2524b2fc195eSAndrew Gallatin 2525b2fc195eSAndrew Gallatin abort_with_alloc: 25266d87a65dSAndrew Gallatin mxge_free_rings(sc); 2527b2fc195eSAndrew Gallatin 2528b2fc195eSAndrew Gallatin abort_with_nothing: 2529b2fc195eSAndrew Gallatin return err; 2530b2fc195eSAndrew Gallatin } 2531b2fc195eSAndrew Gallatin 2532053e637fSAndrew Gallatin static void 2533053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs) 2534053e637fSAndrew Gallatin { 2535053e637fSAndrew Gallatin int bufsize = mtu + ETHER_HDR_LEN + 4 + MXGEFW_PAD; 2536053e637fSAndrew Gallatin 2537053e637fSAndrew Gallatin if (bufsize < MCLBYTES) { 2538053e637fSAndrew Gallatin /* easy, everything fits in a single buffer */ 2539053e637fSAndrew Gallatin *big_buf_size = MCLBYTES; 2540053e637fSAndrew Gallatin *cl_size = MCLBYTES; 2541053e637fSAndrew Gallatin *nbufs = 1; 2542053e637fSAndrew Gallatin return; 2543053e637fSAndrew Gallatin } 2544053e637fSAndrew Gallatin 2545053e637fSAndrew Gallatin if (bufsize < MJUMPAGESIZE) { 2546053e637fSAndrew Gallatin /* still easy, everything still fits in a single buffer */ 2547053e637fSAndrew Gallatin *big_buf_size = MJUMPAGESIZE; 2548053e637fSAndrew Gallatin *cl_size = MJUMPAGESIZE; 2549053e637fSAndrew Gallatin *nbufs = 1; 2550053e637fSAndrew Gallatin return; 2551053e637fSAndrew Gallatin } 2552053e637fSAndrew Gallatin /* now we need to use virtually contiguous buffers */ 2553053e637fSAndrew Gallatin *cl_size = MJUM9BYTES; 2554053e637fSAndrew Gallatin *big_buf_size = 4096; 2555053e637fSAndrew Gallatin *nbufs = mtu / 4096 + 1; 2556053e637fSAndrew Gallatin /* needs to be a power of two, so round up */ 2557053e637fSAndrew Gallatin if (*nbufs == 3) 2558053e637fSAndrew Gallatin *nbufs = 4; 2559053e637fSAndrew Gallatin } 2560053e637fSAndrew Gallatin 2561b2fc195eSAndrew Gallatin static int 25626d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2563b2fc195eSAndrew Gallatin { 25646d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2565053e637fSAndrew Gallatin int i, err, big_bytes; 2566b2fc195eSAndrew Gallatin bus_dmamap_t map; 25670fa7f681SAndrew Gallatin bus_addr_t bus; 2568053e637fSAndrew Gallatin struct lro_entry *lro_entry; 2569b2fc195eSAndrew Gallatin 2570053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_free); 2571053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_active); 2572053e637fSAndrew Gallatin 2573053e637fSAndrew Gallatin for (i = 0; i < sc->lro_cnt; i++) { 2574053e637fSAndrew Gallatin lro_entry = (struct lro_entry *) 2575053e637fSAndrew Gallatin malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 2576053e637fSAndrew Gallatin if (lro_entry == NULL) { 2577053e637fSAndrew Gallatin sc->lro_cnt = i; 2578053e637fSAndrew Gallatin break; 2579053e637fSAndrew Gallatin } 2580053e637fSAndrew Gallatin SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next); 2581053e637fSAndrew Gallatin } 2582b2fc195eSAndrew Gallatin 25837d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 25847d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 25857d542e2dSAndrew Gallatin 25866d87a65dSAndrew Gallatin err = mxge_reset(sc); 2587b2fc195eSAndrew Gallatin if (err != 0) { 2588b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2589b2fc195eSAndrew Gallatin return EIO; 2590b2fc195eSAndrew Gallatin } 2591a98d6cd7SAndrew Gallatin bzero(sc->rx_done.entry, 2592a98d6cd7SAndrew Gallatin mxge_max_intr_slots * sizeof(*sc->rx_done.entry)); 2593b2fc195eSAndrew Gallatin 2594053e637fSAndrew Gallatin mxge_choose_params(sc->ifp->if_mtu, &big_bytes, 2595053e637fSAndrew Gallatin &sc->rx_big.cl_size, &sc->rx_big.nbufs); 2596b2fc195eSAndrew Gallatin 2597053e637fSAndrew Gallatin cmd.data0 = sc->rx_big.nbufs; 2598053e637fSAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 2599053e637fSAndrew Gallatin &cmd); 2600053e637fSAndrew Gallatin /* error is only meaningful if we're trying to set 2601053e637fSAndrew Gallatin MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */ 2602053e637fSAndrew Gallatin if (err && sc->rx_big.nbufs > 1) { 2603053e637fSAndrew Gallatin device_printf(sc->dev, 2604053e637fSAndrew Gallatin "Failed to set alway-use-n to %d\n", 2605053e637fSAndrew Gallatin sc->rx_big.nbufs); 2606053e637fSAndrew Gallatin return EIO; 2607053e637fSAndrew Gallatin } 2608b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2609b2fc195eSAndrew Gallatin 26105e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2611b2fc195eSAndrew Gallatin sc->tx.lanai = 2612b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 26136d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 26145e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2615b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2616b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 26175e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2618b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2619b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2620b2fc195eSAndrew Gallatin 2621b2fc195eSAndrew Gallatin if (err != 0) { 2622b2fc195eSAndrew Gallatin device_printf(sc->dev, 2623b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2624a98d6cd7SAndrew Gallatin return EIO; 2625b2fc195eSAndrew Gallatin } 2626b2fc195eSAndrew Gallatin 2627b2fc195eSAndrew Gallatin if (sc->wc) { 26280fa7f681SAndrew Gallatin sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4; 26290fa7f681SAndrew Gallatin sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL; 26300fa7f681SAndrew Gallatin sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG; 2631b2fc195eSAndrew Gallatin } else { 2632b2fc195eSAndrew Gallatin sc->tx.wc_fifo = 0; 2633b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = 0; 2634b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = 0; 2635b2fc195eSAndrew Gallatin } 2636b2fc195eSAndrew Gallatin 2637b2fc195eSAndrew Gallatin 2638b2fc195eSAndrew Gallatin /* stock receive rings */ 2639b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2640b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 26416d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2642b2fc195eSAndrew Gallatin if (err) { 2643b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2644b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2645b2fc195eSAndrew Gallatin goto abort; 2646b2fc195eSAndrew Gallatin } 2647b2fc195eSAndrew Gallatin } 2648b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2649053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_low = 0xffffffff; 2650053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_high = 0xffffffff; 2651053e637fSAndrew Gallatin } 2652053e637fSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) { 2653b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 26546d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2655b2fc195eSAndrew Gallatin if (err) { 2656b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2657b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2658b2fc195eSAndrew Gallatin goto abort; 2659b2fc195eSAndrew Gallatin } 2660b2fc195eSAndrew Gallatin } 2661b2fc195eSAndrew Gallatin 2662b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2663b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2664b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2665053e637fSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + 4; 26665e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2667b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 26685e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2669b2fc195eSAndrew Gallatin &cmd); 2670053e637fSAndrew Gallatin cmd.data0 = big_bytes; 26715e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 26720fa7f681SAndrew Gallatin 26730fa7f681SAndrew Gallatin if (err != 0) { 26740fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 26750fa7f681SAndrew Gallatin goto abort; 26760fa7f681SAndrew Gallatin } 26770fa7f681SAndrew Gallatin 2678b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 26796d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 26806d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 26810fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 26820fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 26830fa7f681SAndrew Gallatin 26840fa7f681SAndrew Gallatin if (err != 0) { 26850fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 26860fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 26870fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 26880fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 26890fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 26900fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 26910fa7f681SAndrew Gallatin &cmd); 26920fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 26930fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 26940fa7f681SAndrew Gallatin } else { 26950fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 26960fa7f681SAndrew Gallatin } 2697b2fc195eSAndrew Gallatin 2698b2fc195eSAndrew Gallatin if (err != 0) { 2699b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2700b2fc195eSAndrew Gallatin goto abort; 2701b2fc195eSAndrew Gallatin } 2702b2fc195eSAndrew Gallatin 2703b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 27045e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2705b2fc195eSAndrew Gallatin if (err) { 2706b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2707b2fc195eSAndrew Gallatin goto abort; 2708b2fc195eSAndrew Gallatin } 2709b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2710b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2711b2fc195eSAndrew Gallatin 2712b2fc195eSAndrew Gallatin return 0; 2713b2fc195eSAndrew Gallatin 2714b2fc195eSAndrew Gallatin 2715b2fc195eSAndrew Gallatin abort: 27166d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2717a98d6cd7SAndrew Gallatin 2718b2fc195eSAndrew Gallatin return err; 2719b2fc195eSAndrew Gallatin } 2720b2fc195eSAndrew Gallatin 2721b2fc195eSAndrew Gallatin static int 27226d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2723b2fc195eSAndrew Gallatin { 2724053e637fSAndrew Gallatin struct lro_entry *lro_entry; 27256d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2726b2fc195eSAndrew Gallatin int err, old_down_cnt; 2727b2fc195eSAndrew Gallatin 2728b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2729b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2730b2fc195eSAndrew Gallatin mb(); 27315e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2732b2fc195eSAndrew Gallatin if (err) { 2733b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2734b2fc195eSAndrew Gallatin } 2735b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2736b2fc195eSAndrew Gallatin /* wait for down irq */ 2737dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 2738b2fc195eSAndrew Gallatin } 2739b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2740b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2741b2fc195eSAndrew Gallatin } 2742a98d6cd7SAndrew Gallatin 27436d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2744a98d6cd7SAndrew Gallatin 2745053e637fSAndrew Gallatin while (!SLIST_EMPTY(&sc->lro_free)) { 2746053e637fSAndrew Gallatin lro_entry = SLIST_FIRST(&sc->lro_free); 2747053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_free, next); 2748053e637fSAndrew Gallatin } 2749b2fc195eSAndrew Gallatin return 0; 2750b2fc195eSAndrew Gallatin } 2751b2fc195eSAndrew Gallatin 2752dce01b9bSAndrew Gallatin static void 2753dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 2754dce01b9bSAndrew Gallatin { 2755dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2756dce01b9bSAndrew Gallatin int reg; 2757dce01b9bSAndrew Gallatin uint16_t cmd, lnk, pectl; 2758dce01b9bSAndrew Gallatin 2759dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2760dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2761dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2762dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2763dce01b9bSAndrew Gallatin 2764dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2765dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2766dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 2767dce01b9bSAndrew Gallatin } 2768dce01b9bSAndrew Gallatin 2769dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 2770dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 2771dce01b9bSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2772dce01b9bSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2773dce01b9bSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2774dce01b9bSAndrew Gallatin } 2775dce01b9bSAndrew Gallatin 2776dce01b9bSAndrew Gallatin static uint32_t 2777dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 2778dce01b9bSAndrew Gallatin { 2779dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2780dce01b9bSAndrew Gallatin uint32_t vs; 2781dce01b9bSAndrew Gallatin 2782dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 2783dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { 2784dce01b9bSAndrew Gallatin device_printf(sc->dev, 2785dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 2786dce01b9bSAndrew Gallatin return (uint32_t)-1; 2787dce01b9bSAndrew Gallatin } 2788dce01b9bSAndrew Gallatin /* enable read32 mode */ 2789dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 2790dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 2791dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 2792dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 2793dce01b9bSAndrew Gallatin } 2794dce01b9bSAndrew Gallatin 2795dce01b9bSAndrew Gallatin static void 2796dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 2797dce01b9bSAndrew Gallatin { 2798dce01b9bSAndrew Gallatin int err; 2799dce01b9bSAndrew Gallatin uint32_t reboot; 2800dce01b9bSAndrew Gallatin uint16_t cmd; 2801dce01b9bSAndrew Gallatin 2802dce01b9bSAndrew Gallatin err = ENXIO; 2803dce01b9bSAndrew Gallatin 2804dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 2805dce01b9bSAndrew Gallatin 2806dce01b9bSAndrew Gallatin /* 2807dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 2808dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 2809dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 2810dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 2811dce01b9bSAndrew Gallatin * again 2812dce01b9bSAndrew Gallatin */ 2813dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2814dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2815dce01b9bSAndrew Gallatin /* 2816dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 2817dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 2818dce01b9bSAndrew Gallatin * back, then give up 2819dce01b9bSAndrew Gallatin */ 2820dce01b9bSAndrew Gallatin DELAY(1000*100); 2821dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2822dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2823dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 2824dce01b9bSAndrew Gallatin goto abort; 2825dce01b9bSAndrew Gallatin } 2826dce01b9bSAndrew Gallatin } 2827dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 2828dce01b9bSAndrew Gallatin /* print the reboot status */ 2829dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 2830dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 2831dce01b9bSAndrew Gallatin reboot); 2832dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 2833dce01b9bSAndrew Gallatin 2834dce01b9bSAndrew Gallatin /* XXXX waiting for pci_cfg_restore() to be exported */ 2835dce01b9bSAndrew Gallatin goto abort; /* just abort for now */ 2836dce01b9bSAndrew Gallatin 2837dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 2838dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2839dce01b9bSAndrew Gallatin } else { 2840dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC did not reboot, ring state:\n"); 2841dce01b9bSAndrew Gallatin device_printf(sc->dev, "tx.req=%d tx.done=%d\n", 2842dce01b9bSAndrew Gallatin sc->tx.req, sc->tx.done); 2843dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 2844dce01b9bSAndrew Gallatin sc->tx.pkt_done, 2845dce01b9bSAndrew Gallatin be32toh(sc->fw_stats->send_done_count)); 2846dce01b9bSAndrew Gallatin } 2847dce01b9bSAndrew Gallatin 2848dce01b9bSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { 2849dce01b9bSAndrew Gallatin mxge_close(sc); 2850dce01b9bSAndrew Gallatin err = mxge_open(sc); 2851dce01b9bSAndrew Gallatin } 2852dce01b9bSAndrew Gallatin 2853dce01b9bSAndrew Gallatin abort: 2854dce01b9bSAndrew Gallatin /* 2855dce01b9bSAndrew Gallatin * stop the watchdog if the nic is dead, to avoid spamming the 2856dce01b9bSAndrew Gallatin * console 2857dce01b9bSAndrew Gallatin */ 2858dce01b9bSAndrew Gallatin if (err != 0) { 2859dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2860dce01b9bSAndrew Gallatin } 2861dce01b9bSAndrew Gallatin } 2862dce01b9bSAndrew Gallatin 2863dce01b9bSAndrew Gallatin static void 2864dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 2865dce01b9bSAndrew Gallatin { 2866dce01b9bSAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 2867dce01b9bSAndrew Gallatin 2868dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 2869dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 2870dce01b9bSAndrew Gallatin if (tx->req != tx->done && 2871dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 2872dce01b9bSAndrew Gallatin tx->done == tx->watchdog_done) 2873dce01b9bSAndrew Gallatin mxge_watchdog_reset(sc); 2874dce01b9bSAndrew Gallatin 2875dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 2876dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 2877dce01b9bSAndrew Gallatin } 2878dce01b9bSAndrew Gallatin 2879dce01b9bSAndrew Gallatin static void 2880dce01b9bSAndrew Gallatin mxge_tick(void *arg) 2881dce01b9bSAndrew Gallatin { 2882dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 2883dce01b9bSAndrew Gallatin 2884dce01b9bSAndrew Gallatin 2885dce01b9bSAndrew Gallatin /* Synchronize with possible callout reset/stop. */ 2886dce01b9bSAndrew Gallatin if (callout_pending(&sc->co_hdl) || 2887dce01b9bSAndrew Gallatin !callout_active(&sc->co_hdl)) { 2888dce01b9bSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2889dce01b9bSAndrew Gallatin return; 2890dce01b9bSAndrew Gallatin } 2891dce01b9bSAndrew Gallatin 2892dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2893dce01b9bSAndrew Gallatin mxge_watchdog(sc); 2894dce01b9bSAndrew Gallatin } 2895b2fc195eSAndrew Gallatin 2896b2fc195eSAndrew Gallatin static int 28976d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2898b2fc195eSAndrew Gallatin { 2899b2fc195eSAndrew Gallatin return EINVAL; 2900b2fc195eSAndrew Gallatin } 2901b2fc195eSAndrew Gallatin 2902b2fc195eSAndrew Gallatin static int 29036d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2904b2fc195eSAndrew Gallatin { 2905b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2906b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2907b2fc195eSAndrew Gallatin int err = 0; 2908b2fc195eSAndrew Gallatin 2909b2fc195eSAndrew Gallatin 2910b2fc195eSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN; 2911053e637fSAndrew Gallatin if ((real_mtu > sc->max_mtu) || real_mtu < 60) 2912b2fc195eSAndrew Gallatin return EINVAL; 2913a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2914b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2915b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2916b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2917dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 29186d87a65dSAndrew Gallatin mxge_close(sc); 29196d87a65dSAndrew Gallatin err = mxge_open(sc); 2920b2fc195eSAndrew Gallatin if (err != 0) { 2921b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 29226d87a65dSAndrew Gallatin mxge_close(sc); 29236d87a65dSAndrew Gallatin (void) mxge_open(sc); 2924b2fc195eSAndrew Gallatin } 2925dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2926b2fc195eSAndrew Gallatin } 2927a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2928b2fc195eSAndrew Gallatin return err; 2929b2fc195eSAndrew Gallatin } 2930b2fc195eSAndrew Gallatin 2931b2fc195eSAndrew Gallatin static void 29326d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 2933b2fc195eSAndrew Gallatin { 29346d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2935b2fc195eSAndrew Gallatin 2936b2fc195eSAndrew Gallatin 2937b2fc195eSAndrew Gallatin if (sc == NULL) 2938b2fc195eSAndrew Gallatin return; 2939b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 2940b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 2941b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 2942b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 2943b2fc195eSAndrew Gallatin } 2944b2fc195eSAndrew Gallatin 2945b2fc195eSAndrew Gallatin static int 29466d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2947b2fc195eSAndrew Gallatin { 29486d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2949b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 2950b2fc195eSAndrew Gallatin int err, mask; 2951b2fc195eSAndrew Gallatin 2952b2fc195eSAndrew Gallatin err = 0; 2953b2fc195eSAndrew Gallatin switch (command) { 2954b2fc195eSAndrew Gallatin case SIOCSIFADDR: 2955b2fc195eSAndrew Gallatin case SIOCGIFADDR: 2956b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 2957b2fc195eSAndrew Gallatin break; 2958b2fc195eSAndrew Gallatin 2959b2fc195eSAndrew Gallatin case SIOCSIFMTU: 29606d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 2961b2fc195eSAndrew Gallatin break; 2962b2fc195eSAndrew Gallatin 2963b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 2964a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2965b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 2966dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 29676d87a65dSAndrew Gallatin err = mxge_open(sc); 2968dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, 2969dce01b9bSAndrew Gallatin mxge_tick, sc); 2970dce01b9bSAndrew Gallatin } else { 29710fa7f681SAndrew Gallatin /* take care of promis can allmulti 29720fa7f681SAndrew Gallatin flag chages */ 29730fa7f681SAndrew Gallatin mxge_change_promisc(sc, 29740fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 29750fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 29760fa7f681SAndrew Gallatin } 2977b2fc195eSAndrew Gallatin } else { 2978dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 29796d87a65dSAndrew Gallatin mxge_close(sc); 2980dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2981dce01b9bSAndrew Gallatin } 2982b2fc195eSAndrew Gallatin } 2983a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2984b2fc195eSAndrew Gallatin break; 2985b2fc195eSAndrew Gallatin 2986b2fc195eSAndrew Gallatin case SIOCADDMULTI: 2987b2fc195eSAndrew Gallatin case SIOCDELMULTI: 2988a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 29890fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 2990a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2991b2fc195eSAndrew Gallatin break; 2992b2fc195eSAndrew Gallatin 2993b2fc195eSAndrew Gallatin case SIOCSIFCAP: 2994a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2995b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2996b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 2997b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 2998aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 2999aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 3000aed8e389SAndrew Gallatin | CSUM_TSO); 3001b2fc195eSAndrew Gallatin } else { 3002b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 3003b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 3004b2fc195eSAndrew Gallatin } 3005b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 3006b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 3007b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 30085e7d8541SAndrew Gallatin sc->csum_flag = 0; 3009b2fc195eSAndrew Gallatin } else { 3010b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 30115e7d8541SAndrew Gallatin sc->csum_flag = 1; 3012b2fc195eSAndrew Gallatin } 3013b2fc195eSAndrew Gallatin } 3014aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 3015aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 3016aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 3017aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 3018aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 3019aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 3020aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 3021aed8e389SAndrew Gallatin } else { 3022aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 3023aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 3024aed8e389SAndrew Gallatin err = EINVAL; 3025aed8e389SAndrew Gallatin } 3026aed8e389SAndrew Gallatin } 3027a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3028b2fc195eSAndrew Gallatin break; 3029b2fc195eSAndrew Gallatin 3030b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 3031b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 3032b2fc195eSAndrew Gallatin &sc->media, command); 3033b2fc195eSAndrew Gallatin break; 3034b2fc195eSAndrew Gallatin 3035b2fc195eSAndrew Gallatin default: 3036b2fc195eSAndrew Gallatin err = ENOTTY; 3037b2fc195eSAndrew Gallatin } 3038b2fc195eSAndrew Gallatin return err; 3039b2fc195eSAndrew Gallatin } 3040b2fc195eSAndrew Gallatin 3041b2fc195eSAndrew Gallatin static void 30426d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 3043b2fc195eSAndrew Gallatin { 3044b2fc195eSAndrew Gallatin 30456d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 30466d87a65dSAndrew Gallatin &mxge_flow_control); 30476d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 30486d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 30496d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 30506d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 3051d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 3052d91b1b49SAndrew Gallatin &mxge_force_firmware); 30535e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 30545e7d8541SAndrew Gallatin &mxge_deassert_wait); 30555e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 30565e7d8541SAndrew Gallatin &mxge_verbose); 3057dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 3058053e637fSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt); 3059b2fc195eSAndrew Gallatin 30605e7d8541SAndrew Gallatin if (bootverbose) 30615e7d8541SAndrew Gallatin mxge_verbose = 1; 30626d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 30636d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 3064dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 3065dce01b9bSAndrew Gallatin mxge_ticks = hz; 30666d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 3067053e637fSAndrew Gallatin 3068b2fc195eSAndrew Gallatin } 3069b2fc195eSAndrew Gallatin 3070b2fc195eSAndrew Gallatin static int 30716d87a65dSAndrew Gallatin mxge_attach(device_t dev) 3072b2fc195eSAndrew Gallatin { 30736d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3074b2fc195eSAndrew Gallatin struct ifnet *ifp; 3075b2fc195eSAndrew Gallatin size_t bytes; 3076dce01b9bSAndrew Gallatin int count, rid, err; 3077b2fc195eSAndrew Gallatin 3078b2fc195eSAndrew Gallatin sc->dev = dev; 30796d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 3080b2fc195eSAndrew Gallatin 3081b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 3082b2fc195eSAndrew Gallatin 1, /* alignment */ 3083b2fc195eSAndrew Gallatin 4096, /* boundary */ 3084b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 3085b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 3086b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 3087aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 30885e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 3089b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 3090b2fc195eSAndrew Gallatin 0, /* flags */ 3091b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 3092b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 3093b2fc195eSAndrew Gallatin 3094b2fc195eSAndrew Gallatin if (err != 0) { 3095b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 3096b2fc195eSAndrew Gallatin err); 3097b2fc195eSAndrew Gallatin goto abort_with_nothing; 3098b2fc195eSAndrew Gallatin } 3099b2fc195eSAndrew Gallatin 3100b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 3101b2fc195eSAndrew Gallatin if (ifp == NULL) { 3102b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 3103b2fc195eSAndrew Gallatin err = ENOSPC; 3104b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 3105b2fc195eSAndrew Gallatin } 3106a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 3107a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3108a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 3109a98d6cd7SAndrew Gallatin snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", 3110a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3111a98d6cd7SAndrew Gallatin mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); 3112a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 3113a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 3114a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 3115b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 3116b2fc195eSAndrew Gallatin 3117dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 3118d91b1b49SAndrew Gallatin 3119dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 3120b2fc195eSAndrew Gallatin 3121b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 3122b2fc195eSAndrew Gallatin rid = PCIR_BARS; 3123b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 3124b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 3125b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 3126b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 3127b2fc195eSAndrew Gallatin err = ENXIO; 3128b2fc195eSAndrew Gallatin goto abort_with_lock; 3129b2fc195eSAndrew Gallatin } 3130b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 3131b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 3132b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 3133b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 3134b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 3135b2fc195eSAndrew Gallatin err = ENXIO; 3136b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3137b2fc195eSAndrew Gallatin } 3138b2fc195eSAndrew Gallatin 3139b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 3140b2fc195eSAndrew Gallatin lanai SRAM */ 31416d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 3142b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 3143b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 31446d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 3145b2fc195eSAndrew Gallatin sc->eeprom_strings, 31466d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 31476d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 3148b2fc195eSAndrew Gallatin if (err != 0) 3149b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3150b2fc195eSAndrew Gallatin 3151b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 31526d87a65dSAndrew Gallatin mxge_enable_wc(sc); 3153b2fc195eSAndrew Gallatin 3154b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 31556d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 31566d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 3157b2fc195eSAndrew Gallatin if (err != 0) 3158b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3159b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 31606d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 3161b2fc195eSAndrew Gallatin if (err != 0) 3162b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 3163b2fc195eSAndrew Gallatin 31646d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 3165b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 3166b2fc195eSAndrew Gallatin if (err != 0) 3167b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 31685e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 3169b2fc195eSAndrew Gallatin 3170a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 3171a98d6cd7SAndrew Gallatin if (err != 0) 3172a98d6cd7SAndrew Gallatin goto abort_with_fw_stats; 3173b2fc195eSAndrew Gallatin 3174b2fc195eSAndrew Gallatin /* allocate interrupt queues */ 31755e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry); 31765e7d8541SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 3177b2fc195eSAndrew Gallatin if (err != 0) 3178a98d6cd7SAndrew Gallatin goto abort_with_dmabench; 31795e7d8541SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 31805e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 3181dc8731d4SAndrew Gallatin 3182b2fc195eSAndrew Gallatin /* Add our ithread */ 3183dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 3184dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 3185dc8731d4SAndrew Gallatin rid = 1; 3186dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 3187dc8731d4SAndrew Gallatin } else { 3188b2fc195eSAndrew Gallatin rid = 0; 3189dc8731d4SAndrew Gallatin } 3190b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 3191b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 3192b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 3193b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 31945e7d8541SAndrew Gallatin goto abort_with_rx_done; 3195b2fc195eSAndrew Gallatin } 3196d91b1b49SAndrew Gallatin if (mxge_verbose) 3197dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 3198dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 3199dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 32008fe615baSAndrew Gallatin /* select & load the firmware */ 32018fe615baSAndrew Gallatin err = mxge_select_firmware(sc); 3202b2fc195eSAndrew Gallatin if (err != 0) 3203b2fc195eSAndrew Gallatin goto abort_with_irq_res; 32045e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 32056d87a65dSAndrew Gallatin err = mxge_reset(sc); 3206b2fc195eSAndrew Gallatin if (err != 0) 3207b2fc195eSAndrew Gallatin goto abort_with_irq_res; 3208b2fc195eSAndrew Gallatin 3209a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 3210a98d6cd7SAndrew Gallatin if (err != 0) { 3211a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 3212a98d6cd7SAndrew Gallatin goto abort_with_irq_res; 3213a98d6cd7SAndrew Gallatin } 3214a98d6cd7SAndrew Gallatin 3215a98d6cd7SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 3216a98d6cd7SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 3217ef544f63SPaolo Pisati NULL, mxge_intr, sc, &sc->ih); 3218a98d6cd7SAndrew Gallatin if (err != 0) { 3219a98d6cd7SAndrew Gallatin goto abort_with_rings; 3220a98d6cd7SAndrew Gallatin } 3221b2fc195eSAndrew Gallatin /* hook into the network stack */ 3222b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 3223b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 3224053e637fSAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4; 3225053e637fSAndrew Gallatin sc->max_mtu = mxge_max_mtu(sc); 3226053e637fSAndrew Gallatin if (sc->max_mtu >= 9000) 3227053e637fSAndrew Gallatin ifp->if_capabilities |= IFCAP_JUMBO_MTU; 3228053e637fSAndrew Gallatin else 3229053e637fSAndrew Gallatin device_printf(dev, "MTU limited to %d. Install " 3230053e637fSAndrew Gallatin "latest firmware for 9000 byte jumbo support", 3231053e637fSAndrew Gallatin sc->max_mtu - ETHER_HDR_LEN); 3232aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 3233b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 32345e7d8541SAndrew Gallatin sc->csum_flag = 1; 32356d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 3236b2fc195eSAndrew Gallatin ifp->if_softc = sc; 3237b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 32386d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 32396d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 3240b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 3241b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 3242053e637fSAndrew Gallatin if (ifp->if_capabilities & IFCAP_JUMBO_MTU) 32436d87a65dSAndrew Gallatin ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; 3244b2fc195eSAndrew Gallatin 3245b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 32466d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 32476d87a65dSAndrew Gallatin mxge_media_status); 3248b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 32496d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 3250b2fc195eSAndrew Gallatin return 0; 3251b2fc195eSAndrew Gallatin 3252a98d6cd7SAndrew Gallatin abort_with_rings: 3253a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3254b2fc195eSAndrew Gallatin abort_with_irq_res: 3255dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3256dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3257dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3258dc8731d4SAndrew Gallatin pci_release_msi(dev); 32595e7d8541SAndrew Gallatin abort_with_rx_done: 32605e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 32615e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 3262a98d6cd7SAndrew Gallatin abort_with_dmabench: 3263a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 32645e7d8541SAndrew Gallatin abort_with_fw_stats: 32656d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3266b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 32676d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 3268b2fc195eSAndrew Gallatin abort_with_cmd_dma: 32696d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3270b2fc195eSAndrew Gallatin abort_with_mem_res: 3271b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3272b2fc195eSAndrew Gallatin abort_with_lock: 3273b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3274a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3275a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3276a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3277b2fc195eSAndrew Gallatin if_free(ifp); 3278b2fc195eSAndrew Gallatin abort_with_parent_dmat: 3279b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3280b2fc195eSAndrew Gallatin 3281b2fc195eSAndrew Gallatin abort_with_nothing: 3282b2fc195eSAndrew Gallatin return err; 3283b2fc195eSAndrew Gallatin } 3284b2fc195eSAndrew Gallatin 3285b2fc195eSAndrew Gallatin static int 32866d87a65dSAndrew Gallatin mxge_detach(device_t dev) 3287b2fc195eSAndrew Gallatin { 32886d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3289b2fc195eSAndrew Gallatin 3290a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3291b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 32926d87a65dSAndrew Gallatin mxge_close(sc); 3293dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3294a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3295b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 3296dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 3297091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 3298a98d6cd7SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 3299a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3300dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3301dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3302dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3303dc8731d4SAndrew Gallatin pci_release_msi(dev); 3304dc8731d4SAndrew Gallatin 33055e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 33065e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 33076d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3308a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 33096d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 33106d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3311b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3312b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3313a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3314a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3315a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3316b2fc195eSAndrew Gallatin if_free(sc->ifp); 3317b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3318b2fc195eSAndrew Gallatin return 0; 3319b2fc195eSAndrew Gallatin } 3320b2fc195eSAndrew Gallatin 3321b2fc195eSAndrew Gallatin static int 33226d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 3323b2fc195eSAndrew Gallatin { 3324b2fc195eSAndrew Gallatin return 0; 3325b2fc195eSAndrew Gallatin } 3326b2fc195eSAndrew Gallatin 3327b2fc195eSAndrew Gallatin /* 3328b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 3329b2fc195eSAndrew Gallatin 3330b2fc195eSAndrew Gallatin Local Variables: 3331b2fc195eSAndrew Gallatin c-file-style:"linux" 3332b2fc195eSAndrew Gallatin tab-width:8 3333b2fc195eSAndrew Gallatin End: 3334b2fc195eSAndrew Gallatin */ 3335