16d87a65dSAndrew Gallatin /****************************************************************************** 2b2fc195eSAndrew Gallatin 3eb8e82f5SAndrew Gallatin Copyright (c) 2006-2007, 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 12eb8e82f5SAndrew Gallatin 2. Neither the name of the Myricom Inc, nor the names of its 13b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 14b2fc195eSAndrew Gallatin this software without specific prior written permission. 15b2fc195eSAndrew Gallatin 16b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 27b2fc195eSAndrew Gallatin 28b2fc195eSAndrew Gallatin ***************************************************************************/ 29b2fc195eSAndrew Gallatin 30b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 31b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 32b2fc195eSAndrew Gallatin 33b2fc195eSAndrew Gallatin #include <sys/param.h> 34b2fc195eSAndrew Gallatin #include <sys/systm.h> 35b2fc195eSAndrew Gallatin #include <sys/linker.h> 36b2fc195eSAndrew Gallatin #include <sys/firmware.h> 37b2fc195eSAndrew Gallatin #include <sys/endian.h> 38b2fc195eSAndrew Gallatin #include <sys/sockio.h> 39b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 40b2fc195eSAndrew Gallatin #include <sys/malloc.h> 41b2fc195eSAndrew Gallatin #include <sys/kdb.h> 42b2fc195eSAndrew Gallatin #include <sys/kernel.h> 434e7f640dSJohn Baldwin #include <sys/lock.h> 44b2fc195eSAndrew Gallatin #include <sys/module.h> 45b2fc195eSAndrew Gallatin #include <sys/memrange.h> 46b2fc195eSAndrew Gallatin #include <sys/socket.h> 47b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 48b2fc195eSAndrew Gallatin #include <sys/sx.h> 49b2fc195eSAndrew Gallatin 50b2fc195eSAndrew Gallatin #include <net/if.h> 51b2fc195eSAndrew Gallatin #include <net/if_arp.h> 52b2fc195eSAndrew Gallatin #include <net/ethernet.h> 53b2fc195eSAndrew Gallatin #include <net/if_dl.h> 54b2fc195eSAndrew Gallatin #include <net/if_media.h> 55b2fc195eSAndrew Gallatin 56b2fc195eSAndrew Gallatin #include <net/bpf.h> 57b2fc195eSAndrew Gallatin 58b2fc195eSAndrew Gallatin #include <net/if_types.h> 59b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h> 60b2fc195eSAndrew Gallatin #include <net/zlib.h> 61b2fc195eSAndrew Gallatin 62b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 63b2fc195eSAndrew Gallatin #include <netinet/in.h> 64b2fc195eSAndrew Gallatin #include <netinet/ip.h> 65aed8e389SAndrew Gallatin #include <netinet/tcp.h> 66b2fc195eSAndrew Gallatin 67b2fc195eSAndrew Gallatin #include <machine/bus.h> 68053e637fSAndrew Gallatin #include <machine/in_cksum.h> 69b2fc195eSAndrew Gallatin #include <machine/resource.h> 70b2fc195eSAndrew Gallatin #include <sys/bus.h> 71b2fc195eSAndrew Gallatin #include <sys/rman.h> 72b2fc195eSAndrew Gallatin 73b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 74b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 75b2fc195eSAndrew Gallatin 76b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 77b2fc195eSAndrew Gallatin #include <vm/pmap.h> 78b2fc195eSAndrew Gallatin 79c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64) 80c2c14a69SAndrew Gallatin #include <machine/specialreg.h> 81c2c14a69SAndrew Gallatin #endif 82c2c14a69SAndrew Gallatin 836d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 846d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 856d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 86b2fc195eSAndrew Gallatin 87b2fc195eSAndrew Gallatin /* tunable params */ 886d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 89d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0; 906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 926d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 935e7d8541SAndrew Gallatin static int mxge_verbose = 0; 94f04b33f8SAndrew Gallatin static int mxge_lro_cnt = 8; 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); 130276edd10SAndrew Gallatin static int mxge_close(mxge_softc_t *sc); 131276edd10SAndrew Gallatin static int mxge_open(mxge_softc_t *sc); 132276edd10SAndrew Gallatin static void mxge_tick(void *arg); 1338fe615baSAndrew Gallatin 134b2fc195eSAndrew Gallatin static int 1356d87a65dSAndrew Gallatin mxge_probe(device_t dev) 136b2fc195eSAndrew Gallatin { 1376d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1386d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 139b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 140b2fc195eSAndrew Gallatin return 0; 141b2fc195eSAndrew Gallatin } 142b2fc195eSAndrew Gallatin return ENXIO; 143b2fc195eSAndrew Gallatin } 144b2fc195eSAndrew Gallatin 145b2fc195eSAndrew Gallatin static void 1466d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 147b2fc195eSAndrew Gallatin { 148b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 149b2fc195eSAndrew Gallatin vm_paddr_t pa; 150b2fc195eSAndrew Gallatin vm_offset_t len; 151b2fc195eSAndrew Gallatin int err, action; 152b2fc195eSAndrew Gallatin 1534d69a9d0SAndrew Gallatin sc->wc = 1; 154b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 155c2c14a69SAndrew Gallatin #if defined(__i386) || defined(__amd64) 156c2c14a69SAndrew Gallatin err = pmap_change_attr((vm_offset_t) sc->sram, 157c2c14a69SAndrew Gallatin len, PAT_WRITE_COMBINING); 158c2c14a69SAndrew Gallatin if (err == 0) 159c2c14a69SAndrew Gallatin return; 160c2c14a69SAndrew Gallatin else 161c2c14a69SAndrew Gallatin device_printf(sc->dev, "pmap_change_attr failed, %d\n", 162c2c14a69SAndrew Gallatin err); 163c2c14a69SAndrew Gallatin #endif 164c2c14a69SAndrew Gallatin pa = rman_get_start(sc->mem_res); 165b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 166b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 167b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 168b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1696d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 170b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 171b2fc195eSAndrew Gallatin if (err != 0) { 1724d69a9d0SAndrew Gallatin sc->wc = 0; 173b2fc195eSAndrew Gallatin device_printf(sc->dev, 174b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 175b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 176b2fc195eSAndrew Gallatin } 177b2fc195eSAndrew Gallatin } 178b2fc195eSAndrew Gallatin 179b2fc195eSAndrew Gallatin 180b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 181b2fc195eSAndrew Gallatin static void 1826d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 183b2fc195eSAndrew Gallatin int error) 184b2fc195eSAndrew Gallatin { 185b2fc195eSAndrew Gallatin if (error == 0) { 186b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 187b2fc195eSAndrew Gallatin } 188b2fc195eSAndrew Gallatin } 189b2fc195eSAndrew Gallatin 190b2fc195eSAndrew Gallatin static int 1916d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 192b2fc195eSAndrew Gallatin bus_size_t alignment) 193b2fc195eSAndrew Gallatin { 194b2fc195eSAndrew Gallatin int err; 195b2fc195eSAndrew Gallatin device_t dev = sc->dev; 196b2fc195eSAndrew Gallatin 197b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 198b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 199b2fc195eSAndrew Gallatin alignment, /* alignment */ 200b2fc195eSAndrew Gallatin 4096, /* boundary */ 201b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 202b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 203b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 204b2fc195eSAndrew Gallatin bytes, /* maxsize */ 205b2fc195eSAndrew Gallatin 1, /* num segs */ 206b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 207b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 208b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 209b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 210b2fc195eSAndrew Gallatin if (err != 0) { 211b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 212b2fc195eSAndrew Gallatin return err; 213b2fc195eSAndrew Gallatin } 214b2fc195eSAndrew Gallatin 215b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 216b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 217b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 218b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 219b2fc195eSAndrew Gallatin if (err != 0) { 220b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 221b2fc195eSAndrew Gallatin goto abort_with_dmat; 222b2fc195eSAndrew Gallatin } 223b2fc195eSAndrew Gallatin 224b2fc195eSAndrew Gallatin /* load the memory */ 225b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2266d87a65dSAndrew Gallatin mxge_dmamap_callback, 227b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 228b2fc195eSAndrew Gallatin if (err != 0) { 229b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 230b2fc195eSAndrew Gallatin goto abort_with_mem; 231b2fc195eSAndrew Gallatin } 232b2fc195eSAndrew Gallatin return 0; 233b2fc195eSAndrew Gallatin 234b2fc195eSAndrew Gallatin abort_with_mem: 235b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 236b2fc195eSAndrew Gallatin abort_with_dmat: 237b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 238b2fc195eSAndrew Gallatin return err; 239b2fc195eSAndrew Gallatin } 240b2fc195eSAndrew Gallatin 241b2fc195eSAndrew Gallatin 242b2fc195eSAndrew Gallatin static void 2436d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 244b2fc195eSAndrew Gallatin { 245b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 246b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 247b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 248b2fc195eSAndrew Gallatin } 249b2fc195eSAndrew Gallatin 250b2fc195eSAndrew Gallatin /* 251b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 252b2fc195eSAndrew Gallatin * SN=x\0 253b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 254b2fc195eSAndrew Gallatin * PC=text\0 255b2fc195eSAndrew Gallatin */ 256b2fc195eSAndrew Gallatin 257b2fc195eSAndrew Gallatin static int 2586d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 259b2fc195eSAndrew Gallatin { 2606d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 261b2fc195eSAndrew Gallatin 262b2fc195eSAndrew Gallatin char *ptr, *limit; 263b2fc195eSAndrew Gallatin int i, found_mac; 264b2fc195eSAndrew Gallatin 265b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2666d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 267b2fc195eSAndrew Gallatin found_mac = 0; 268b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 269b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2705e7d8541SAndrew Gallatin ptr += 1; 271b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 272b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2735e7d8541SAndrew Gallatin ptr += 3; 274b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 275b2fc195eSAndrew Gallatin goto abort; 276b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 277b2fc195eSAndrew Gallatin found_mac = 1; 278b2fc195eSAndrew Gallatin } 2795e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2805e7d8541SAndrew Gallatin ptr += 3; 2815e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2825e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2835e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2845e7d8541SAndrew Gallatin ptr += 3; 2855e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2865e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 287b2fc195eSAndrew Gallatin } 2886d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 289b2fc195eSAndrew Gallatin } 290b2fc195eSAndrew Gallatin 291b2fc195eSAndrew Gallatin if (found_mac) 292b2fc195eSAndrew Gallatin return 0; 293b2fc195eSAndrew Gallatin 294b2fc195eSAndrew Gallatin abort: 295b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 296b2fc195eSAndrew Gallatin 297b2fc195eSAndrew Gallatin return ENXIO; 298b2fc195eSAndrew Gallatin } 299b2fc195eSAndrew Gallatin 300b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 3018fe615baSAndrew Gallatin static void 3028fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc) 303b2fc195eSAndrew Gallatin { 304b2fc195eSAndrew Gallatin uint32_t val; 3058fe615baSAndrew Gallatin unsigned long base, off; 306b2fc195eSAndrew Gallatin char *va, *cfgptr; 3078fe615baSAndrew Gallatin device_t pdev, mcp55; 3088fe615baSAndrew Gallatin uint16_t vendor_id, device_id, word; 309b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 310b2fc195eSAndrew Gallatin uint32_t *ptr32; 311b2fc195eSAndrew Gallatin 3128fe615baSAndrew Gallatin 3138fe615baSAndrew Gallatin if (!mxge_nvidia_ecrc_enable) 3148fe615baSAndrew Gallatin return; 3158fe615baSAndrew Gallatin 3168fe615baSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 3178fe615baSAndrew Gallatin if (pdev == NULL) { 3188fe615baSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 3198fe615baSAndrew Gallatin return; 3208fe615baSAndrew Gallatin } 3218fe615baSAndrew Gallatin vendor_id = pci_read_config(pdev, PCIR_VENDOR, 2); 3228fe615baSAndrew Gallatin device_id = pci_read_config(pdev, PCIR_DEVICE, 2); 3238fe615baSAndrew Gallatin 3248fe615baSAndrew Gallatin if (vendor_id != 0x10de) 3258fe615baSAndrew Gallatin return; 3268fe615baSAndrew Gallatin 3278fe615baSAndrew Gallatin base = 0; 3288fe615baSAndrew Gallatin 3298fe615baSAndrew Gallatin if (device_id == 0x005d) { 3308fe615baSAndrew Gallatin /* ck804, base address is magic */ 3318fe615baSAndrew Gallatin base = 0xe0000000UL; 3328fe615baSAndrew Gallatin } else if (device_id >= 0x0374 && device_id <= 0x378) { 3338fe615baSAndrew Gallatin /* mcp55, base address stored in chipset */ 3348fe615baSAndrew Gallatin mcp55 = pci_find_bsf(0, 0, 0); 3358fe615baSAndrew Gallatin if (mcp55 && 3368fe615baSAndrew Gallatin 0x10de == pci_read_config(mcp55, PCIR_VENDOR, 2) && 3378fe615baSAndrew Gallatin 0x0369 == pci_read_config(mcp55, PCIR_DEVICE, 2)) { 3388fe615baSAndrew Gallatin word = pci_read_config(mcp55, 0x90, 2); 3398fe615baSAndrew Gallatin base = ((unsigned long)word & 0x7ffeU) << 25; 3408fe615baSAndrew Gallatin } 3418fe615baSAndrew Gallatin } 3428fe615baSAndrew Gallatin if (!base) 3438fe615baSAndrew Gallatin return; 3448fe615baSAndrew Gallatin 345b2fc195eSAndrew Gallatin /* XXXX 346b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 347b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 348b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 349b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 350b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 351b2fc195eSAndrew Gallatin */ 352b2fc195eSAndrew Gallatin #if 0 353b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 354b2fc195eSAndrew Gallatin config space */ 355b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 356b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 357b2fc195eSAndrew Gallatin val |= 0x40; 358b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 3598fe615baSAndrew Gallatin return; 360b2fc195eSAndrew Gallatin } 361b2fc195eSAndrew Gallatin #endif 362b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 363b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 364b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 365b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 366b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 367b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 368b2fc195eSAndrew Gallatin */ 369b2fc195eSAndrew Gallatin 370b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 371b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 372b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 373b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 374b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 375b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 376b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 377b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 378b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 379b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 380b2fc195eSAndrew Gallatin 3818fe615baSAndrew Gallatin off = base 382b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 383b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 384b2fc195eSAndrew Gallatin + 8 * slot); 385b2fc195eSAndrew Gallatin 386b2fc195eSAndrew Gallatin /* map it into the kernel */ 387b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 388b2fc195eSAndrew Gallatin 389b2fc195eSAndrew Gallatin 390b2fc195eSAndrew Gallatin if (va == NULL) { 391b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 3928fe615baSAndrew Gallatin return; 393b2fc195eSAndrew Gallatin } 394b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 395b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 396b2fc195eSAndrew Gallatin 397b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 398b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 399b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 400b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 401b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 402b2fc195eSAndrew Gallatin vendor_id, device_id); 403b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4048fe615baSAndrew Gallatin return; 405b2fc195eSAndrew Gallatin } 406b2fc195eSAndrew Gallatin 407b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 408b2fc195eSAndrew Gallatin val = *ptr32; 409b2fc195eSAndrew Gallatin 410b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 411b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 412b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4138fe615baSAndrew Gallatin return; 414b2fc195eSAndrew Gallatin } 415b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 416b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 4175e7d8541SAndrew Gallatin if (mxge_verbose) 418b2fc195eSAndrew Gallatin device_printf(sc->dev, 4195e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 4205e7d8541SAndrew Gallatin "at %d:%d:%d\n", 421b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 4228fe615baSAndrew Gallatin return; 423b2fc195eSAndrew Gallatin } 424b2fc195eSAndrew Gallatin #else 4258fe615baSAndrew Gallatin static void 4266d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 427b2fc195eSAndrew Gallatin { 428b2fc195eSAndrew Gallatin device_printf(sc->dev, 429b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 4308fe615baSAndrew Gallatin return; 431b2fc195eSAndrew Gallatin } 432b2fc195eSAndrew Gallatin #endif 4338fe615baSAndrew Gallatin 4348fe615baSAndrew Gallatin 4358fe615baSAndrew Gallatin static int 4368fe615baSAndrew Gallatin mxge_dma_test(mxge_softc_t *sc, int test_type) 4378fe615baSAndrew Gallatin { 4388fe615baSAndrew Gallatin mxge_cmd_t cmd; 4398fe615baSAndrew Gallatin bus_addr_t dmatest_bus = sc->dmabench_dma.bus_addr; 4408fe615baSAndrew Gallatin int status; 4418fe615baSAndrew Gallatin uint32_t len; 4428fe615baSAndrew Gallatin char *test = " "; 4438fe615baSAndrew Gallatin 4448fe615baSAndrew Gallatin 4458fe615baSAndrew Gallatin /* Run a small DMA test. 4468fe615baSAndrew Gallatin * The magic multipliers to the length tell the firmware 4478fe615baSAndrew Gallatin * to do DMA read, write, or read+write tests. The 4488fe615baSAndrew Gallatin * results are returned in cmd.data0. The upper 16 4498fe615baSAndrew Gallatin * bits of the return is the number of transfers completed. 4508fe615baSAndrew Gallatin * The lower 16 bits is the time in 0.5us ticks that the 4518fe615baSAndrew Gallatin * transfers took to complete. 4528fe615baSAndrew Gallatin */ 4538fe615baSAndrew Gallatin 4548fe615baSAndrew Gallatin len = sc->tx.boundary; 4558fe615baSAndrew Gallatin 4568fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4578fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4588fe615baSAndrew Gallatin cmd.data2 = len * 0x10000; 4598fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4608fe615baSAndrew Gallatin if (status != 0) { 4618fe615baSAndrew Gallatin test = "read"; 4628fe615baSAndrew Gallatin goto abort; 4638fe615baSAndrew Gallatin } 4648fe615baSAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * len * 2) / 4658fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4668fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4678fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4688fe615baSAndrew Gallatin cmd.data2 = len * 0x1; 4698fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4708fe615baSAndrew Gallatin if (status != 0) { 4718fe615baSAndrew Gallatin test = "write"; 4728fe615baSAndrew Gallatin goto abort; 4738fe615baSAndrew Gallatin } 4748fe615baSAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * len * 2) / 4758fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4768fe615baSAndrew Gallatin 4778fe615baSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmatest_bus); 4788fe615baSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmatest_bus); 4798fe615baSAndrew Gallatin cmd.data2 = len * 0x10001; 4808fe615baSAndrew Gallatin status = mxge_send_cmd(sc, test_type, &cmd); 4818fe615baSAndrew Gallatin if (status != 0) { 4828fe615baSAndrew Gallatin test = "read/write"; 4838fe615baSAndrew Gallatin goto abort; 4848fe615baSAndrew Gallatin } 4858fe615baSAndrew Gallatin sc->read_write_dma = ((cmd.data0>>16) * len * 2 * 2) / 4868fe615baSAndrew Gallatin (cmd.data0 & 0xffff); 4878fe615baSAndrew Gallatin 4888fe615baSAndrew Gallatin abort: 4898fe615baSAndrew Gallatin if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST) 4908fe615baSAndrew Gallatin device_printf(sc->dev, "DMA %s benchmark failed: %d\n", 4918fe615baSAndrew Gallatin test, status); 4928fe615baSAndrew Gallatin 4938fe615baSAndrew Gallatin return status; 4948fe615baSAndrew Gallatin } 4958fe615baSAndrew Gallatin 496b2fc195eSAndrew Gallatin /* 497b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 498b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 499b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 500b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 501b2fc195eSAndrew Gallatin * ECRC generation (if supported). 502b2fc195eSAndrew Gallatin * 503b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 504b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 505b2fc195eSAndrew Gallatin * 506b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 507b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 508b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 509b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 510b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 511b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 512b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 513b2fc195eSAndrew Gallatin */ 514b2fc195eSAndrew Gallatin 5158fe615baSAndrew Gallatin static int 5168fe615baSAndrew Gallatin mxge_firmware_probe(mxge_softc_t *sc) 5178fe615baSAndrew Gallatin { 5188fe615baSAndrew Gallatin device_t dev = sc->dev; 5198fe615baSAndrew Gallatin int reg, status; 5208fe615baSAndrew Gallatin uint16_t pectl; 5218fe615baSAndrew Gallatin 5228fe615baSAndrew Gallatin sc->tx.boundary = 4096; 5238fe615baSAndrew Gallatin /* 5248fe615baSAndrew Gallatin * Verify the max read request size was set to 4KB 5258fe615baSAndrew Gallatin * before trying the test with 4KB. 5268fe615baSAndrew Gallatin */ 5278fe615baSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 5288fe615baSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 5298fe615baSAndrew Gallatin if ((pectl & (5 << 12)) != (5 << 12)) { 5308fe615baSAndrew Gallatin device_printf(dev, "Max Read Req. size != 4k (0x%x\n", 5318fe615baSAndrew Gallatin pectl); 5328fe615baSAndrew Gallatin sc->tx.boundary = 2048; 5338fe615baSAndrew Gallatin } 5348fe615baSAndrew Gallatin } 5358fe615baSAndrew Gallatin 5368fe615baSAndrew Gallatin /* 5378fe615baSAndrew Gallatin * load the optimized firmware (which assumes aligned PCIe 5388fe615baSAndrew Gallatin * completions) in order to see if it works on this host. 5398fe615baSAndrew Gallatin */ 5408fe615baSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 5418fe615baSAndrew Gallatin status = mxge_load_firmware(sc); 5428fe615baSAndrew Gallatin if (status != 0) { 5438fe615baSAndrew Gallatin return status; 5448fe615baSAndrew Gallatin } 5458fe615baSAndrew Gallatin 5468fe615baSAndrew Gallatin /* 5478fe615baSAndrew Gallatin * Enable ECRC if possible 5488fe615baSAndrew Gallatin */ 5498fe615baSAndrew Gallatin mxge_enable_nvidia_ecrc(sc); 5508fe615baSAndrew Gallatin 5518fe615baSAndrew Gallatin /* 5528fe615baSAndrew Gallatin * Run a DMA test which watches for unaligned completions and 5538fe615baSAndrew Gallatin * aborts on the first one seen. 5548fe615baSAndrew Gallatin */ 5558fe615baSAndrew Gallatin 5568fe615baSAndrew Gallatin status = mxge_dma_test(sc, MXGEFW_CMD_UNALIGNED_TEST); 5578fe615baSAndrew Gallatin if (status == 0) 5588fe615baSAndrew Gallatin return 0; /* keep the aligned firmware */ 5598fe615baSAndrew Gallatin 5608fe615baSAndrew Gallatin if (status != E2BIG) 5618fe615baSAndrew Gallatin device_printf(dev, "DMA test failed: %d\n", status); 5628fe615baSAndrew Gallatin if (status == ENOSYS) 5638fe615baSAndrew Gallatin device_printf(dev, "Falling back to ethp! " 5648fe615baSAndrew Gallatin "Please install up to date fw\n"); 5658fe615baSAndrew Gallatin return status; 5668fe615baSAndrew Gallatin } 5678fe615baSAndrew Gallatin 5688fe615baSAndrew Gallatin static int 5696d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 570b2fc195eSAndrew Gallatin { 5718fe615baSAndrew Gallatin int aligned = 0; 572b2fc195eSAndrew Gallatin 573d91b1b49SAndrew Gallatin 574d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 575d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 576d91b1b49SAndrew Gallatin aligned = 1; 577d91b1b49SAndrew Gallatin else 578d91b1b49SAndrew Gallatin aligned = 0; 579d91b1b49SAndrew Gallatin if (mxge_verbose) 580d91b1b49SAndrew Gallatin device_printf(sc->dev, 581d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 582d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 583d91b1b49SAndrew Gallatin goto abort; 584d91b1b49SAndrew Gallatin } 585d91b1b49SAndrew Gallatin 586d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 587d91b1b49SAndrew Gallatin firmware and skip any checks */ 588d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 589d91b1b49SAndrew Gallatin device_printf(sc->dev, 590d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 591d91b1b49SAndrew Gallatin sc->link_width); 592d91b1b49SAndrew Gallatin aligned = 1; 593d91b1b49SAndrew Gallatin goto abort; 594d91b1b49SAndrew Gallatin } 595d91b1b49SAndrew Gallatin 5968fe615baSAndrew Gallatin if (0 == mxge_firmware_probe(sc)) 5978fe615baSAndrew Gallatin return 0; 598b2fc195eSAndrew Gallatin 599b2fc195eSAndrew Gallatin abort: 600b2fc195eSAndrew Gallatin if (aligned) { 6016d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 602b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 603b2fc195eSAndrew Gallatin } else { 6046d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 605b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 606b2fc195eSAndrew Gallatin } 6078fe615baSAndrew Gallatin return (mxge_load_firmware(sc)); 608b2fc195eSAndrew Gallatin } 609b2fc195eSAndrew Gallatin 610b2fc195eSAndrew Gallatin union qualhack 611b2fc195eSAndrew Gallatin { 612b2fc195eSAndrew Gallatin const char *ro_char; 613b2fc195eSAndrew Gallatin char *rw_char; 614b2fc195eSAndrew Gallatin }; 615b2fc195eSAndrew Gallatin 6164da0d523SAndrew Gallatin static int 6174da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 6184da0d523SAndrew Gallatin { 619b824b7d8SAndrew Gallatin 6204da0d523SAndrew Gallatin 6214da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 6224da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 6234da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 6244da0d523SAndrew Gallatin return EIO; 6254da0d523SAndrew Gallatin } 6264da0d523SAndrew Gallatin 6274da0d523SAndrew Gallatin /* save firmware version for sysctl */ 6284da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 6294da0d523SAndrew Gallatin if (mxge_verbose) 6304da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 6314da0d523SAndrew Gallatin 632b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 633b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 6344da0d523SAndrew Gallatin 635b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 636b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 6374da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 6384da0d523SAndrew Gallatin sc->fw_version); 6394da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 6404da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 6414da0d523SAndrew Gallatin return EINVAL; 6424da0d523SAndrew Gallatin } 6434da0d523SAndrew Gallatin return 0; 6444da0d523SAndrew Gallatin 6454da0d523SAndrew Gallatin } 646b2fc195eSAndrew Gallatin 647b2fc195eSAndrew Gallatin static int 6486d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 649b2fc195eSAndrew Gallatin { 65033d54970SLuigi Rizzo const struct firmware *fw; 651b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 652b2fc195eSAndrew Gallatin unsigned hdr_offset; 653b2fc195eSAndrew Gallatin const char *fw_data; 654b2fc195eSAndrew Gallatin union qualhack hack; 655b2fc195eSAndrew Gallatin int status; 6564da0d523SAndrew Gallatin unsigned int i; 6574da0d523SAndrew Gallatin char dummy; 658b2fc195eSAndrew Gallatin 659b2fc195eSAndrew Gallatin 660b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 661b2fc195eSAndrew Gallatin 662b2fc195eSAndrew Gallatin if (fw == NULL) { 663b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 664b2fc195eSAndrew Gallatin sc->fw_name); 665b2fc195eSAndrew Gallatin return ENOENT; 666b2fc195eSAndrew Gallatin } 667b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 668b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 669b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 670b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 671b2fc195eSAndrew Gallatin status = ENOSPC; 672b2fc195eSAndrew Gallatin goto abort_with_fw; 673b2fc195eSAndrew Gallatin } 674b2fc195eSAndrew Gallatin *limit = fw->datasize; 675b2fc195eSAndrew Gallatin 676b2fc195eSAndrew Gallatin /* check id */ 677b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 678b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 679b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 680b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 681b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 682b2fc195eSAndrew Gallatin status = EIO; 683b2fc195eSAndrew Gallatin goto abort_with_fw; 684b2fc195eSAndrew Gallatin } 685b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 686b2fc195eSAndrew Gallatin 6874da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 6884da0d523SAndrew Gallatin if (status != 0) 6894da0d523SAndrew Gallatin goto abort_with_fw; 690b2fc195eSAndrew Gallatin 691b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 692b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 6934da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 6944da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 6954da0d523SAndrew Gallatin hack.rw_char + i, 6964da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 6974da0d523SAndrew Gallatin mb(); 6984da0d523SAndrew Gallatin dummy = *sc->sram; 6994da0d523SAndrew Gallatin mb(); 7004da0d523SAndrew Gallatin } 701b2fc195eSAndrew Gallatin 702b2fc195eSAndrew Gallatin status = 0; 703b2fc195eSAndrew Gallatin abort_with_fw: 704b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 705b2fc195eSAndrew Gallatin return status; 706b2fc195eSAndrew Gallatin } 707b2fc195eSAndrew Gallatin 708b2fc195eSAndrew Gallatin /* 709b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 710b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 711b2fc195eSAndrew Gallatin */ 712b2fc195eSAndrew Gallatin 713b2fc195eSAndrew Gallatin static void 7146d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 715b2fc195eSAndrew Gallatin { 716b2fc195eSAndrew Gallatin char buf_bytes[72]; 717b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 718b2fc195eSAndrew Gallatin volatile char *submit; 719b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 720b2fc195eSAndrew Gallatin int i; 721b2fc195eSAndrew Gallatin 722b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 723b2fc195eSAndrew Gallatin 724b2fc195eSAndrew Gallatin /* clear confirmation addr */ 725b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 726b2fc195eSAndrew Gallatin *confirm = 0; 727b2fc195eSAndrew Gallatin mb(); 728b2fc195eSAndrew Gallatin 729b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 730b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 731b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 732b2fc195eSAndrew Gallatin */ 733b2fc195eSAndrew Gallatin 7346d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7356d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 736b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 737b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 738b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 7396d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 7406d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 741b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 742b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 743b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 744b2fc195eSAndrew Gallatin 745b2fc195eSAndrew Gallatin 7460fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 747b2fc195eSAndrew Gallatin 7486d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 749b2fc195eSAndrew Gallatin mb(); 750b2fc195eSAndrew Gallatin DELAY(1000); 751b2fc195eSAndrew Gallatin mb(); 752b2fc195eSAndrew Gallatin i = 0; 753b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 754b2fc195eSAndrew Gallatin DELAY(1000); 755b2fc195eSAndrew Gallatin i++; 756b2fc195eSAndrew Gallatin } 757b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 758b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 759b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 760b2fc195eSAndrew Gallatin *confirm); 761b2fc195eSAndrew Gallatin } 762b2fc195eSAndrew Gallatin return; 763b2fc195eSAndrew Gallatin } 764b2fc195eSAndrew Gallatin 765b2fc195eSAndrew Gallatin static int 7666d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 767b2fc195eSAndrew Gallatin { 768b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 769b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 770b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 7710fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 772b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 773e0501fd0SAndrew Gallatin int err, sleep_total = 0; 774b2fc195eSAndrew Gallatin 775b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 776b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 777b2fc195eSAndrew Gallatin 778b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 779b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 780b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 781b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 7826d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7836d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 784b2fc195eSAndrew Gallatin 785b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 786b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 787a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 788b2fc195eSAndrew Gallatin response->result = 0xffffffff; 789b2fc195eSAndrew Gallatin mb(); 7906d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 791b2fc195eSAndrew Gallatin 7925e7d8541SAndrew Gallatin /* wait up to 20ms */ 793e0501fd0SAndrew Gallatin err = EAGAIN; 7945e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 795b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 796b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 797b2fc195eSAndrew Gallatin mb(); 798e0501fd0SAndrew Gallatin switch (be32toh(response->result)) { 799e0501fd0SAndrew Gallatin case 0: 800b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 801e0501fd0SAndrew Gallatin err = 0; 802e0501fd0SAndrew Gallatin break; 803e0501fd0SAndrew Gallatin case 0xffffffff: 804e0501fd0SAndrew Gallatin DELAY(1000); 805e0501fd0SAndrew Gallatin break; 806e0501fd0SAndrew Gallatin case MXGEFW_CMD_UNKNOWN: 807e0501fd0SAndrew Gallatin err = ENOSYS; 808e0501fd0SAndrew Gallatin break; 809e0501fd0SAndrew Gallatin case MXGEFW_CMD_ERROR_UNALIGNED: 810e0501fd0SAndrew Gallatin err = E2BIG; 811e0501fd0SAndrew Gallatin break; 812e0501fd0SAndrew Gallatin default: 813b2fc195eSAndrew Gallatin device_printf(sc->dev, 8146d87a65dSAndrew Gallatin "mxge: command %d " 815b2fc195eSAndrew Gallatin "failed, result = %d\n", 816b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 817e0501fd0SAndrew Gallatin err = ENXIO; 818e0501fd0SAndrew Gallatin break; 819b2fc195eSAndrew Gallatin } 820e0501fd0SAndrew Gallatin if (err != EAGAIN) 821e0501fd0SAndrew Gallatin break; 822b2fc195eSAndrew Gallatin } 823e0501fd0SAndrew Gallatin if (err == EAGAIN) 8246d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 825b2fc195eSAndrew Gallatin "result = %d\n", 826b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 827e0501fd0SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 828e0501fd0SAndrew Gallatin return err; 829b2fc195eSAndrew Gallatin } 830b2fc195eSAndrew Gallatin 8314da0d523SAndrew Gallatin static int 8324da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 8334da0d523SAndrew Gallatin { 8344da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 8354da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 8364da0d523SAndrew Gallatin size_t hdr_offset; 8374da0d523SAndrew Gallatin int status; 8384da0d523SAndrew Gallatin 8394da0d523SAndrew Gallatin /* find running firmware header */ 8404da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 8414da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 8424da0d523SAndrew Gallatin 8434da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 8444da0d523SAndrew Gallatin device_printf(sc->dev, 8454da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 8464da0d523SAndrew Gallatin (int)hdr_offset); 8474da0d523SAndrew Gallatin return EIO; 8484da0d523SAndrew Gallatin } 8494da0d523SAndrew Gallatin 8504da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 8514da0d523SAndrew Gallatin * validate firmware */ 8524da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 8534da0d523SAndrew Gallatin if (hdr == NULL) { 8544da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 8554da0d523SAndrew Gallatin return ENOMEM; 8564da0d523SAndrew Gallatin } 8574da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 8584da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 8594da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 8604da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 8614da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 862b824b7d8SAndrew Gallatin 863b824b7d8SAndrew Gallatin /* 864b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 865b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 866b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 867b824b7d8SAndrew Gallatin */ 868b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 869b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 870b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 871b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 872b824b7d8SAndrew Gallatin "working around rx filter bug\n", 873b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 874b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 875b824b7d8SAndrew Gallatin } 876b824b7d8SAndrew Gallatin 8774da0d523SAndrew Gallatin return status; 8784da0d523SAndrew Gallatin } 8794da0d523SAndrew Gallatin 880b2fc195eSAndrew Gallatin 881b2fc195eSAndrew Gallatin static int 8826d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 883b2fc195eSAndrew Gallatin { 884b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 885b2fc195eSAndrew Gallatin volatile char *submit; 886b2fc195eSAndrew Gallatin char buf_bytes[72]; 887b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 888b2fc195eSAndrew Gallatin int status, i; 889b2fc195eSAndrew Gallatin 890b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 891b2fc195eSAndrew Gallatin 892b2fc195eSAndrew Gallatin size = sc->sram_size; 8936d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 894b2fc195eSAndrew Gallatin if (status) { 8954da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 8964da0d523SAndrew Gallatin it is new enough */ 8974da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 8984da0d523SAndrew Gallatin if (status) { 8994da0d523SAndrew Gallatin device_printf(sc->dev, 9004da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 901b2fc195eSAndrew Gallatin return status; 902b2fc195eSAndrew Gallatin } 9034da0d523SAndrew Gallatin device_printf(sc->dev, 9044da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 9054da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 9064da0d523SAndrew Gallatin device_printf(sc->dev, 9074da0d523SAndrew Gallatin "Using firmware currently running on NIC" 9084da0d523SAndrew Gallatin ". For optimal\n"); 9094da0d523SAndrew Gallatin device_printf(sc->dev, 9104da0d523SAndrew Gallatin "performance consider loading optimized " 9114da0d523SAndrew Gallatin "firmware\n"); 9124da0d523SAndrew Gallatin } 913d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 914d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 915d91b1b49SAndrew Gallatin return 0; 9164da0d523SAndrew Gallatin } 917b2fc195eSAndrew Gallatin /* clear confirmation addr */ 918b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 919b2fc195eSAndrew Gallatin *confirm = 0; 920b2fc195eSAndrew Gallatin mb(); 921b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 922b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 923b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 924b2fc195eSAndrew Gallatin */ 925b2fc195eSAndrew Gallatin 9266d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 9276d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 928b2fc195eSAndrew Gallatin 929b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 930b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 931b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 932b2fc195eSAndrew Gallatin 933b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 934b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 935b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 936b2fc195eSAndrew Gallatin */ 937b2fc195eSAndrew Gallatin /* where the code starts*/ 9386d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 939b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 940b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 941b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 942b2fc195eSAndrew Gallatin 9430fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 9446d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 945b2fc195eSAndrew Gallatin mb(); 946b2fc195eSAndrew Gallatin DELAY(1000); 947b2fc195eSAndrew Gallatin mb(); 948b2fc195eSAndrew Gallatin i = 0; 949b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 950b2fc195eSAndrew Gallatin DELAY(1000*10); 951b2fc195eSAndrew Gallatin i++; 952b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 953b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 954b2fc195eSAndrew Gallatin } 955b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 956b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 957b2fc195eSAndrew Gallatin confirm, *confirm); 958b2fc195eSAndrew Gallatin 959b2fc195eSAndrew Gallatin return ENXIO; 960b2fc195eSAndrew Gallatin } 961b2fc195eSAndrew Gallatin return 0; 962b2fc195eSAndrew Gallatin } 963b2fc195eSAndrew Gallatin 964b2fc195eSAndrew Gallatin static int 9656d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 966b2fc195eSAndrew Gallatin { 9676d87a65dSAndrew Gallatin mxge_cmd_t cmd; 968b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 969b2fc195eSAndrew Gallatin int status; 970b2fc195eSAndrew Gallatin 971b2fc195eSAndrew Gallatin 972b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 973b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 974b2fc195eSAndrew Gallatin 975b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 976b2fc195eSAndrew Gallatin 9775e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 978b2fc195eSAndrew Gallatin return status; 979b2fc195eSAndrew Gallatin } 980b2fc195eSAndrew Gallatin 981b2fc195eSAndrew Gallatin static int 9826d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 983b2fc195eSAndrew Gallatin { 9846d87a65dSAndrew Gallatin mxge_cmd_t cmd; 985b2fc195eSAndrew Gallatin int status; 986b2fc195eSAndrew Gallatin 987b2fc195eSAndrew Gallatin if (pause) 9885e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 989b2fc195eSAndrew Gallatin &cmd); 990b2fc195eSAndrew Gallatin else 9915e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 992b2fc195eSAndrew Gallatin &cmd); 993b2fc195eSAndrew Gallatin 994b2fc195eSAndrew Gallatin if (status) { 995b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 996b2fc195eSAndrew Gallatin return ENXIO; 997b2fc195eSAndrew Gallatin } 998b2fc195eSAndrew Gallatin sc->pause = pause; 999b2fc195eSAndrew Gallatin return 0; 1000b2fc195eSAndrew Gallatin } 1001b2fc195eSAndrew Gallatin 1002b2fc195eSAndrew Gallatin static void 10036d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 1004b2fc195eSAndrew Gallatin { 10056d87a65dSAndrew Gallatin mxge_cmd_t cmd; 1006b2fc195eSAndrew Gallatin int status; 1007b2fc195eSAndrew Gallatin 1008b2fc195eSAndrew Gallatin if (promisc) 10095e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 1010b2fc195eSAndrew Gallatin &cmd); 1011b2fc195eSAndrew Gallatin else 10125e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 1013b2fc195eSAndrew Gallatin &cmd); 1014b2fc195eSAndrew Gallatin 1015b2fc195eSAndrew Gallatin if (status) { 1016b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 1017b2fc195eSAndrew Gallatin } 1018b2fc195eSAndrew Gallatin } 1019b2fc195eSAndrew Gallatin 10200fa7f681SAndrew Gallatin static void 10210fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 10220fa7f681SAndrew Gallatin { 10230fa7f681SAndrew Gallatin mxge_cmd_t cmd; 10240fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 10250fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 10260fa7f681SAndrew Gallatin int err; 10270fa7f681SAndrew Gallatin 10280fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 10290fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 10300fa7f681SAndrew Gallatin return; 10310fa7f681SAndrew Gallatin 10320fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 10330fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 10340fa7f681SAndrew Gallatin if (err != 0) { 10350fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 10360fa7f681SAndrew Gallatin " error status: %d\n", err); 10370fa7f681SAndrew Gallatin return; 10380fa7f681SAndrew Gallatin } 10390fa7f681SAndrew Gallatin 1040b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 1041b824b7d8SAndrew Gallatin return; 10420fa7f681SAndrew Gallatin 10430fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 10440fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 10450fa7f681SAndrew Gallatin return; 10460fa7f681SAndrew Gallatin 10470fa7f681SAndrew Gallatin /* Flush all the filters */ 10480fa7f681SAndrew Gallatin 10490fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 10500fa7f681SAndrew Gallatin if (err != 0) { 10510fa7f681SAndrew Gallatin device_printf(sc->dev, 10520fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 10530fa7f681SAndrew Gallatin ", error status: %d\n", err); 10540fa7f681SAndrew Gallatin return; 10550fa7f681SAndrew Gallatin } 10560fa7f681SAndrew Gallatin 10570fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 10580fa7f681SAndrew Gallatin 10590fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 10600fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 10610fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 10620fa7f681SAndrew Gallatin continue; 10630fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 10640fa7f681SAndrew Gallatin &cmd.data0, 4); 10650fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 10660fa7f681SAndrew Gallatin &cmd.data1, 2); 10670fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 10680fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 10690fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 10700fa7f681SAndrew Gallatin if (err != 0) { 10710fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 10720fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 10730fa7f681SAndrew Gallatin "%d\t", err); 10740fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 10750fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10760fa7f681SAndrew Gallatin return; 10770fa7f681SAndrew Gallatin } 10780fa7f681SAndrew Gallatin } 10790fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 10800fa7f681SAndrew Gallatin /* Enable multicast filtering */ 10810fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 10820fa7f681SAndrew Gallatin if (err != 0) { 10830fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 10840fa7f681SAndrew Gallatin ", error status: %d\n", err); 10850fa7f681SAndrew Gallatin } 10860fa7f681SAndrew Gallatin } 10870fa7f681SAndrew Gallatin 1088b2fc195eSAndrew Gallatin static int 1089053e637fSAndrew Gallatin mxge_max_mtu(mxge_softc_t *sc) 1090053e637fSAndrew Gallatin { 1091053e637fSAndrew Gallatin mxge_cmd_t cmd; 1092053e637fSAndrew Gallatin int status; 1093053e637fSAndrew Gallatin 1094c792928fSAndrew Gallatin if (MJUMPAGESIZE - MXGEFW_PAD > MXGEFW_MAX_MTU) 1095c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1096053e637fSAndrew Gallatin 1097053e637fSAndrew Gallatin /* try to set nbufs to see if it we can 1098053e637fSAndrew Gallatin use virtually contiguous jumbos */ 1099053e637fSAndrew Gallatin cmd.data0 = 0; 1100053e637fSAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 1101053e637fSAndrew Gallatin &cmd); 1102053e637fSAndrew Gallatin if (status == 0) 1103c792928fSAndrew Gallatin return MXGEFW_MAX_MTU - MXGEFW_PAD; 1104053e637fSAndrew Gallatin 1105053e637fSAndrew Gallatin /* otherwise, we're limited to MJUMPAGESIZE */ 1106053e637fSAndrew Gallatin return MJUMPAGESIZE - MXGEFW_PAD; 1107053e637fSAndrew Gallatin } 1108053e637fSAndrew Gallatin 1109053e637fSAndrew Gallatin static int 1110adae7080SAndrew Gallatin mxge_reset(mxge_softc_t *sc, int interrupts_setup) 1111b2fc195eSAndrew Gallatin { 1112b2fc195eSAndrew Gallatin 11136d87a65dSAndrew Gallatin mxge_cmd_t cmd; 11145e7d8541SAndrew Gallatin size_t bytes; 11155e7d8541SAndrew Gallatin int status; 1116b2fc195eSAndrew Gallatin 1117b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 1118b2fc195eSAndrew Gallatin is alive */ 1119b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 11205e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 1121b2fc195eSAndrew Gallatin if (status != 0) { 1122b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 1123b2fc195eSAndrew Gallatin return ENXIO; 1124b2fc195eSAndrew Gallatin } 1125b2fc195eSAndrew Gallatin 1126091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 1127091feecdSAndrew Gallatin 1128adae7080SAndrew Gallatin if (interrupts_setup) { 1129b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 1130adae7080SAndrew Gallatin bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); 11315e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 11325e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 11335e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 11345e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 11355e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 11365e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 1137adae7080SAndrew Gallatin } 1138b2fc195eSAndrew Gallatin 11396d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 11405e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 11415e7d8541SAndrew Gallatin 11425e7d8541SAndrew Gallatin 11435e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 11445e7d8541SAndrew Gallatin 11455e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 11465e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 11475e7d8541SAndrew Gallatin 11485e7d8541SAndrew Gallatin 11495e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 11506d87a65dSAndrew Gallatin &cmd); 11515e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 1152b2fc195eSAndrew Gallatin if (status != 0) { 1153b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 1154b2fc195eSAndrew Gallatin return status; 1155b2fc195eSAndrew Gallatin } 1156b2fc195eSAndrew Gallatin 11575e7d8541SAndrew Gallatin 11585e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 11595e7d8541SAndrew Gallatin 11605e7d8541SAndrew Gallatin 11615e7d8541SAndrew Gallatin /* run a DMA benchmark */ 11628fe615baSAndrew Gallatin (void) mxge_dma_test(sc, MXGEFW_DMA_TEST); 11635e7d8541SAndrew Gallatin 1164b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 11655e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 11665e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 1167b2fc195eSAndrew Gallatin sc->tx.req = 0; 1168b2fc195eSAndrew Gallatin sc->tx.done = 0; 11695e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 1170a82c2581SAndrew Gallatin sc->tx.wake = 0; 1171adae7080SAndrew Gallatin sc->tx_defrag = 0; 1172a82c2581SAndrew Gallatin sc->tx.stall = 0; 1173b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1174b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1175b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 1176a98d6cd7SAndrew Gallatin sc->fw_stats->valid = 0; 1177a98d6cd7SAndrew Gallatin sc->fw_stats->send_done_count = 0; 1178053e637fSAndrew Gallatin sc->lro_bad_csum = 0; 1179053e637fSAndrew Gallatin sc->lro_queued = 0; 1180053e637fSAndrew Gallatin sc->lro_flushed = 0; 11816d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 11826d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 11836d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 11840fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1185b2fc195eSAndrew Gallatin return status; 1186b2fc195eSAndrew Gallatin } 1187b2fc195eSAndrew Gallatin 1188b2fc195eSAndrew Gallatin static int 11896d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1190b2fc195eSAndrew Gallatin { 11916d87a65dSAndrew Gallatin mxge_softc_t *sc; 1192b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1193b2fc195eSAndrew Gallatin int err; 1194b2fc195eSAndrew Gallatin 1195b2fc195eSAndrew Gallatin sc = arg1; 1196b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1197b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1198b2fc195eSAndrew Gallatin if (err != 0) { 1199b2fc195eSAndrew Gallatin return err; 1200b2fc195eSAndrew Gallatin } 1201b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1202b2fc195eSAndrew Gallatin return 0; 1203b2fc195eSAndrew Gallatin 1204b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1205b2fc195eSAndrew Gallatin return EINVAL; 1206b2fc195eSAndrew Gallatin 1207a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 12085e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1209b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 12105e7d8541SAndrew Gallatin 1211a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1212b2fc195eSAndrew Gallatin return err; 1213b2fc195eSAndrew Gallatin } 1214b2fc195eSAndrew Gallatin 1215b2fc195eSAndrew Gallatin static int 12166d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1217b2fc195eSAndrew Gallatin { 12186d87a65dSAndrew Gallatin mxge_softc_t *sc; 1219b2fc195eSAndrew Gallatin unsigned int enabled; 1220b2fc195eSAndrew Gallatin int err; 1221b2fc195eSAndrew Gallatin 1222b2fc195eSAndrew Gallatin sc = arg1; 1223b2fc195eSAndrew Gallatin enabled = sc->pause; 1224b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1225b2fc195eSAndrew Gallatin if (err != 0) { 1226b2fc195eSAndrew Gallatin return err; 1227b2fc195eSAndrew Gallatin } 1228b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1229b2fc195eSAndrew Gallatin return 0; 1230b2fc195eSAndrew Gallatin 1231a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 12326d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1233a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1234b2fc195eSAndrew Gallatin return err; 1235b2fc195eSAndrew Gallatin } 1236b2fc195eSAndrew Gallatin 1237b2fc195eSAndrew Gallatin static int 1238f04b33f8SAndrew Gallatin mxge_change_lro_locked(mxge_softc_t *sc, int lro_cnt) 1239f04b33f8SAndrew Gallatin { 1240f04b33f8SAndrew Gallatin struct ifnet *ifp; 1241f04b33f8SAndrew Gallatin int err; 1242f04b33f8SAndrew Gallatin 1243f04b33f8SAndrew Gallatin ifp = sc->ifp; 1244f04b33f8SAndrew Gallatin if (lro_cnt == 0) 1245f04b33f8SAndrew Gallatin ifp->if_capenable &= ~IFCAP_LRO; 1246f04b33f8SAndrew Gallatin else 1247f04b33f8SAndrew Gallatin ifp->if_capenable |= IFCAP_LRO; 1248f04b33f8SAndrew Gallatin sc->lro_cnt = lro_cnt; 1249f04b33f8SAndrew Gallatin callout_stop(&sc->co_hdl); 1250f04b33f8SAndrew Gallatin mxge_close(sc); 1251f04b33f8SAndrew Gallatin err = mxge_open(sc); 1252f04b33f8SAndrew Gallatin if (err == 0) 1253f04b33f8SAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 1254f04b33f8SAndrew Gallatin return err; 1255f04b33f8SAndrew Gallatin } 1256f04b33f8SAndrew Gallatin 1257f04b33f8SAndrew Gallatin static int 1258276edd10SAndrew Gallatin mxge_change_lro(SYSCTL_HANDLER_ARGS) 1259276edd10SAndrew Gallatin { 1260276edd10SAndrew Gallatin mxge_softc_t *sc; 1261276edd10SAndrew Gallatin unsigned int lro_cnt; 1262276edd10SAndrew Gallatin int err; 1263276edd10SAndrew Gallatin 1264276edd10SAndrew Gallatin sc = arg1; 1265276edd10SAndrew Gallatin lro_cnt = sc->lro_cnt; 1266276edd10SAndrew Gallatin err = sysctl_handle_int(oidp, &lro_cnt, arg2, req); 1267276edd10SAndrew Gallatin if (err != 0) 1268276edd10SAndrew Gallatin return err; 1269276edd10SAndrew Gallatin 1270276edd10SAndrew Gallatin if (lro_cnt == sc->lro_cnt) 1271276edd10SAndrew Gallatin return 0; 1272276edd10SAndrew Gallatin 1273276edd10SAndrew Gallatin if (lro_cnt > 128) 1274276edd10SAndrew Gallatin return EINVAL; 1275276edd10SAndrew Gallatin 1276276edd10SAndrew Gallatin mtx_lock(&sc->driver_mtx); 1277f04b33f8SAndrew Gallatin err = mxge_change_lro_locked(sc, lro_cnt); 1278276edd10SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1279276edd10SAndrew Gallatin return err; 1280276edd10SAndrew Gallatin } 1281276edd10SAndrew Gallatin 1282276edd10SAndrew Gallatin static int 12836d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1284b2fc195eSAndrew Gallatin { 1285b2fc195eSAndrew Gallatin int err; 1286b2fc195eSAndrew Gallatin 1287b2fc195eSAndrew Gallatin if (arg1 == NULL) 1288b2fc195eSAndrew Gallatin return EFAULT; 1289b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1290b2fc195eSAndrew Gallatin arg1 = NULL; 1291b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1292b2fc195eSAndrew Gallatin 1293b2fc195eSAndrew Gallatin return err; 1294b2fc195eSAndrew Gallatin } 1295b2fc195eSAndrew Gallatin 1296b2fc195eSAndrew Gallatin static void 12976d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1298b2fc195eSAndrew Gallatin { 1299b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1300b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 13015e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1302b2fc195eSAndrew Gallatin 1303b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1304b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1305b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1306b2fc195eSAndrew Gallatin 13075e7d8541SAndrew Gallatin /* random information */ 13085e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 13095e7d8541SAndrew Gallatin "firmware_version", 13105e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 13115e7d8541SAndrew Gallatin 0, "firmware version"); 13125e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 13135e7d8541SAndrew Gallatin "serial_number", 13145e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 13155e7d8541SAndrew Gallatin 0, "serial number"); 13165e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 13175e7d8541SAndrew Gallatin "product_code", 13185e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 13195e7d8541SAndrew Gallatin 0, "product_code"); 13205e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1321d91b1b49SAndrew Gallatin "pcie_link_width", 1322d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1323d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1324d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13255e7d8541SAndrew Gallatin "tx_boundary", 13265e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 13275e7d8541SAndrew Gallatin 0, "tx_boundary"); 13285e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1329091feecdSAndrew Gallatin "write_combine", 1330091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1331091feecdSAndrew Gallatin 0, "write combining PIO?"); 1332091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13335e7d8541SAndrew Gallatin "read_dma_MBs", 13345e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 13355e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 13365e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13375e7d8541SAndrew Gallatin "write_dma_MBs", 13385e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 13395e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 13405e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13415e7d8541SAndrew Gallatin "read_write_dma_MBs", 13425e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 13435e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 13445e7d8541SAndrew Gallatin 13455e7d8541SAndrew Gallatin 13465e7d8541SAndrew Gallatin /* performance related tunables */ 1347b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1348b2fc195eSAndrew Gallatin "intr_coal_delay", 1349b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 13506d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1351b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1352b2fc195eSAndrew Gallatin 1353b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1354b2fc195eSAndrew Gallatin "flow_control_enabled", 1355b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 13566d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1357b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1358b2fc195eSAndrew Gallatin 1359b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 13605e7d8541SAndrew Gallatin "deassert_wait", 13615e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 13625e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1363b2fc195eSAndrew Gallatin 1364b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1365b2fc195eSAndrew Gallatin Need to swap it */ 1366b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1367b2fc195eSAndrew Gallatin "link_up", 1368b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 13696d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1370b2fc195eSAndrew Gallatin "I", "link up"); 1371b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1372b2fc195eSAndrew Gallatin "rdma_tags_available", 1373b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 13746d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1375b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1376b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1377adae7080SAndrew Gallatin "dropped_bad_crc32", 1378adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1379adae7080SAndrew Gallatin &fw->dropped_bad_crc32, 13806d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1381adae7080SAndrew Gallatin "I", "dropped_bad_crc32"); 1382adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1383adae7080SAndrew Gallatin "dropped_bad_phy", 1384adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1385adae7080SAndrew Gallatin &fw->dropped_bad_phy, 1386adae7080SAndrew Gallatin 0, mxge_handle_be32, 1387adae7080SAndrew Gallatin "I", "dropped_bad_phy"); 1388b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1389b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1390b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1391b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 13926d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1393b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1394b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1395adae7080SAndrew Gallatin "dropped_link_overflow", 1396adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 1397adae7080SAndrew Gallatin 0, mxge_handle_be32, 1398adae7080SAndrew Gallatin "I", "dropped_link_overflow"); 1399adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 14000fa7f681SAndrew Gallatin "dropped_multicast_filtered", 14010fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 14020fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 14030fa7f681SAndrew Gallatin 0, mxge_handle_be32, 14040fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 14050fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1406adae7080SAndrew Gallatin "dropped_no_big_buffer", 1407adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 14086d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1409adae7080SAndrew Gallatin "I", "dropped_no_big_buffer"); 1410b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1411b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1412b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1413b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 14146d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1415b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1416b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1417adae7080SAndrew Gallatin "dropped_overrun", 1418adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 14196d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1420adae7080SAndrew Gallatin "I", "dropped_overrun"); 1421adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1422adae7080SAndrew Gallatin "dropped_pause", 1423adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1424adae7080SAndrew Gallatin &fw->dropped_pause, 1425adae7080SAndrew Gallatin 0, mxge_handle_be32, 1426adae7080SAndrew Gallatin "I", "dropped_pause"); 1427adae7080SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1428adae7080SAndrew Gallatin "dropped_runt", 1429adae7080SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 1430adae7080SAndrew Gallatin 0, mxge_handle_be32, 1431adae7080SAndrew Gallatin "I", "dropped_runt"); 1432b2fc195eSAndrew Gallatin 1433a0394e33SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1434a0394e33SAndrew Gallatin "dropped_unicast_filtered", 1435a0394e33SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_unicast_filtered, 1436a0394e33SAndrew Gallatin 0, mxge_handle_be32, 1437a0394e33SAndrew Gallatin "I", "dropped_unicast_filtered"); 1438a0394e33SAndrew Gallatin 1439b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1440b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14415e7d8541SAndrew Gallatin "rx_small_cnt", 14425e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 14435e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 14445e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14455e7d8541SAndrew Gallatin "rx_big_cnt", 14465e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 14475e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 14485e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1449b2fc195eSAndrew Gallatin "tx_req", 1450b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1451b2fc195eSAndrew Gallatin 0, "tx_req"); 1452b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1453b2fc195eSAndrew Gallatin "tx_done", 1454b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1455b2fc195eSAndrew Gallatin 0, "tx_done"); 1456b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14575e7d8541SAndrew Gallatin "tx_pkt_done", 14585e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 14595e7d8541SAndrew Gallatin 0, "tx_done"); 1460a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1461a82c2581SAndrew Gallatin "tx_stall", 1462a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.stall, 1463a82c2581SAndrew Gallatin 0, "tx_stall"); 1464a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1465a82c2581SAndrew Gallatin "tx_wake", 1466a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.wake, 1467a82c2581SAndrew Gallatin 0, "tx_wake"); 1468adae7080SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1469adae7080SAndrew Gallatin "tx_defrag", 1470adae7080SAndrew Gallatin CTLFLAG_RD, &sc->tx_defrag, 1471adae7080SAndrew Gallatin 0, "tx_defrag"); 14725e7d8541SAndrew Gallatin 14735e7d8541SAndrew Gallatin /* verbose printing? */ 1474b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 14755e7d8541SAndrew Gallatin "verbose", 14765e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 14775e7d8541SAndrew Gallatin 0, "verbose printing"); 1478b2fc195eSAndrew Gallatin 1479053e637fSAndrew Gallatin /* lro */ 1480276edd10SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1481276edd10SAndrew Gallatin "lro_cnt", 1482276edd10SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 1483276edd10SAndrew Gallatin 0, mxge_change_lro, 1484276edd10SAndrew Gallatin "I", "number of lro merge queues"); 1485053e637fSAndrew Gallatin 1486053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1487053e637fSAndrew Gallatin "lro_flushed", CTLFLAG_RD, &sc->lro_flushed, 1488053e637fSAndrew Gallatin 0, "number of lro merge queues flushed"); 1489053e637fSAndrew Gallatin 1490053e637fSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1491053e637fSAndrew Gallatin "lro_queued", CTLFLAG_RD, &sc->lro_queued, 1492053e637fSAndrew Gallatin 0, "number of frames appended to lro merge queues"); 1493053e637fSAndrew Gallatin 1494b2fc195eSAndrew Gallatin } 1495b2fc195eSAndrew Gallatin 1496b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1497b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1498b2fc195eSAndrew Gallatin 1499b2fc195eSAndrew Gallatin static inline void 15006d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1501b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1502b2fc195eSAndrew Gallatin { 1503b2fc195eSAndrew Gallatin int idx, starting_slot; 1504b2fc195eSAndrew Gallatin starting_slot = tx->req; 1505b2fc195eSAndrew Gallatin while (cnt > 1) { 1506b2fc195eSAndrew Gallatin cnt--; 1507b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 15086d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1509b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1510b2fc195eSAndrew Gallatin mb(); 1511b2fc195eSAndrew Gallatin } 1512b2fc195eSAndrew Gallatin } 1513b2fc195eSAndrew Gallatin 1514b2fc195eSAndrew Gallatin /* 1515b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1516b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1517b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1518b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1519b2fc195eSAndrew Gallatin */ 1520b2fc195eSAndrew Gallatin 1521b2fc195eSAndrew Gallatin static inline void 15226d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1523b2fc195eSAndrew Gallatin int cnt) 1524b2fc195eSAndrew Gallatin { 1525b2fc195eSAndrew Gallatin int idx, i; 1526b2fc195eSAndrew Gallatin uint32_t *src_ints; 1527b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1528b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1529b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 15305e7d8541SAndrew Gallatin uint8_t last_flags; 1531b2fc195eSAndrew Gallatin 1532b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1533b2fc195eSAndrew Gallatin 15345e7d8541SAndrew Gallatin last_flags = src->flags; 15355e7d8541SAndrew Gallatin src->flags = 0; 1536b2fc195eSAndrew Gallatin mb(); 1537b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1538b2fc195eSAndrew Gallatin srcp = src; 1539b2fc195eSAndrew Gallatin 1540b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1541b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 15426d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1543b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1544b2fc195eSAndrew Gallatin srcp += 2; 1545b2fc195eSAndrew Gallatin dstp += 2; 1546b2fc195eSAndrew Gallatin } 1547b2fc195eSAndrew Gallatin } else { 1548b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1549b2fc195eSAndrew Gallatin that it is submitted below */ 15506d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1551b2fc195eSAndrew Gallatin i = 0; 1552b2fc195eSAndrew Gallatin } 1553b2fc195eSAndrew Gallatin if (i < cnt) { 1554b2fc195eSAndrew Gallatin /* submit the first request */ 15556d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1556b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1557b2fc195eSAndrew Gallatin } 1558b2fc195eSAndrew Gallatin 1559b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 15605e7d8541SAndrew Gallatin src->flags = last_flags; 1561b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1562b2fc195eSAndrew Gallatin src_ints+=3; 1563b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1564b2fc195eSAndrew Gallatin dst_ints+=3; 1565b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1566b2fc195eSAndrew Gallatin tx->req += cnt; 1567b2fc195eSAndrew Gallatin mb(); 1568b2fc195eSAndrew Gallatin } 1569b2fc195eSAndrew Gallatin 1570b2fc195eSAndrew Gallatin static void 1571c792928fSAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt, 1572c792928fSAndrew Gallatin int ip_off) 1573aed8e389SAndrew Gallatin { 1574aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1575aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1576aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1577aed8e389SAndrew Gallatin struct ip *ip; 1578aed8e389SAndrew Gallatin struct tcphdr *tcp; 1579aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1580aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1581aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1582aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1583aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1584aed8e389SAndrew Gallatin static int once; 1585aed8e389SAndrew Gallatin 1586aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1587aed8e389SAndrew Gallatin 1588aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1589aed8e389SAndrew Gallatin * send loop that we are still in the 1590aed8e389SAndrew Gallatin * header portion of the TSO packet. 1591aed8e389SAndrew Gallatin */ 1592aed8e389SAndrew Gallatin 1593aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1594aed8e389SAndrew Gallatin header together in the first mbuf, copy 1595aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1596c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { 1597c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + sizeof (*ip), 1598aed8e389SAndrew Gallatin sc->scratch); 1599c792928fSAndrew Gallatin ip = (struct ip *)(sc->scratch + ip_off); 1600aed8e389SAndrew Gallatin } else { 1601c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1602aed8e389SAndrew Gallatin } 1603c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + (ip->ip_hl << 2) 1604aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1605c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + (ip->ip_hl << 2) 1606aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1607c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1608aed8e389SAndrew Gallatin } 1609aed8e389SAndrew Gallatin 1610aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1611c792928fSAndrew Gallatin cum_len = -(ip_off + ((ip->ip_hl + tcp->th_off) << 2)); 1612aed8e389SAndrew Gallatin 1613aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1614c792928fSAndrew Gallatin cksum_offset = ip_off + (ip->ip_hl << 2); 1615aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1616aed8e389SAndrew Gallatin 1617aed8e389SAndrew Gallatin 1618aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1619aed8e389SAndrew Gallatin * The firmware figures out where to put 1620aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1621aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1622aed8e389SAndrew Gallatin 1623aed8e389SAndrew Gallatin tx = &sc->tx; 1624aed8e389SAndrew Gallatin req = tx->req_list; 1625aed8e389SAndrew Gallatin seg = tx->seg_list; 1626aed8e389SAndrew Gallatin cnt = 0; 1627aed8e389SAndrew Gallatin rdma_count = 0; 1628aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1629aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1630aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1631aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1632aed8e389SAndrew Gallatin * to 0 after a segment cut. 1633aed8e389SAndrew Gallatin * 1634aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1635aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1636aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1637aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1638aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1639aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1640aed8e389SAndrew Gallatin * 1641aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1642aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1643aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1644aed8e389SAndrew Gallatin */ 1645aed8e389SAndrew Gallatin 1646aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1647aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1648aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1649aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1650e39a0a37SAndrew Gallatin len = seg->ds_len; 1651aed8e389SAndrew Gallatin 1652aed8e389SAndrew Gallatin while (len) { 1653aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1654e39a0a37SAndrew Gallatin seglen = len; 1655aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1656aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1657aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1658aed8e389SAndrew Gallatin /* payload */ 1659aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1660aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1661aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1662aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1663aed8e389SAndrew Gallatin flags_next |= next_is_first * 1664aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1665aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1666aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1667aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1668aed8e389SAndrew Gallatin /* header ends */ 1669aed8e389SAndrew Gallatin rdma_count = -1; 1670aed8e389SAndrew Gallatin cum_len_next = 0; 1671aed8e389SAndrew Gallatin seglen = -cum_len; 1672aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1673aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1674aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1675aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1676aed8e389SAndrew Gallatin } 1677aed8e389SAndrew Gallatin 1678aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1679aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1680aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1681aed8e389SAndrew Gallatin req->pad = 0; 1682aed8e389SAndrew Gallatin req->rdma_count = 1; 1683aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1684aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1685aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1686aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1687aed8e389SAndrew Gallatin low += seglen; 1688aed8e389SAndrew Gallatin len -= seglen; 1689aed8e389SAndrew Gallatin cum_len = cum_len_next; 1690aed8e389SAndrew Gallatin flags = flags_next; 1691aed8e389SAndrew Gallatin req++; 1692aed8e389SAndrew Gallatin cnt++; 1693aed8e389SAndrew Gallatin rdma_count++; 1694aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1695aed8e389SAndrew Gallatin cksum_offset -= seglen; 1696aed8e389SAndrew Gallatin else 1697aed8e389SAndrew Gallatin cksum_offset = 0; 1698adae7080SAndrew Gallatin if (__predict_false(cnt > tx->max_desc)) 1699aed8e389SAndrew Gallatin goto drop; 1700aed8e389SAndrew Gallatin } 1701aed8e389SAndrew Gallatin busdma_seg_cnt--; 1702aed8e389SAndrew Gallatin seg++; 1703aed8e389SAndrew Gallatin } 1704aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1705aed8e389SAndrew Gallatin 1706aed8e389SAndrew Gallatin do { 1707aed8e389SAndrew Gallatin req--; 1708aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1709aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1710aed8e389SAndrew Gallatin 1711aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1712aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1713aed8e389SAndrew Gallatin return; 1714aed8e389SAndrew Gallatin 1715aed8e389SAndrew Gallatin drop: 1716e39a0a37SAndrew Gallatin bus_dmamap_unload(tx->dmat, tx->info[tx->req & tx->mask].map); 1717aed8e389SAndrew Gallatin m_freem(m); 1718aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1719aed8e389SAndrew Gallatin if (!once) { 1720adae7080SAndrew Gallatin printf("tx->max_desc exceeded via TSO!\n"); 1721adae7080SAndrew Gallatin printf("mss = %d, %ld, %d!\n", mss, 1722adae7080SAndrew Gallatin (long)seg - (long)tx->seg_list, tx->max_desc); 1723aed8e389SAndrew Gallatin once = 1; 1724aed8e389SAndrew Gallatin } 1725aed8e389SAndrew Gallatin return; 1726aed8e389SAndrew Gallatin 1727aed8e389SAndrew Gallatin } 1728aed8e389SAndrew Gallatin 1729c792928fSAndrew Gallatin /* 1730c792928fSAndrew Gallatin * We reproduce the software vlan tag insertion from 1731c792928fSAndrew Gallatin * net/if_vlan.c:vlan_start() here so that we can advertise "hardware" 1732c792928fSAndrew Gallatin * vlan tag insertion. We need to advertise this in order to have the 1733c792928fSAndrew Gallatin * vlan interface respect our csum offload flags. 1734c792928fSAndrew Gallatin */ 1735c792928fSAndrew Gallatin static struct mbuf * 1736c792928fSAndrew Gallatin mxge_vlan_tag_insert(struct mbuf *m) 1737c792928fSAndrew Gallatin { 1738c792928fSAndrew Gallatin struct ether_vlan_header *evl; 1739c792928fSAndrew Gallatin 1740c792928fSAndrew Gallatin M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); 1741c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1742c792928fSAndrew Gallatin return NULL; 1743c792928fSAndrew Gallatin if (m->m_len < sizeof(*evl)) { 1744c792928fSAndrew Gallatin m = m_pullup(m, sizeof(*evl)); 1745c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1746c792928fSAndrew Gallatin return NULL; 1747c792928fSAndrew Gallatin } 1748c792928fSAndrew Gallatin /* 1749c792928fSAndrew Gallatin * Transform the Ethernet header into an Ethernet header 1750c792928fSAndrew Gallatin * with 802.1Q encapsulation. 1751c792928fSAndrew Gallatin */ 1752c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 1753c792928fSAndrew Gallatin bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, 1754c792928fSAndrew Gallatin (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); 1755c792928fSAndrew Gallatin evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 1756c792928fSAndrew Gallatin evl->evl_tag = htons(m->m_pkthdr.ether_vtag); 1757c792928fSAndrew Gallatin m->m_flags &= ~M_VLANTAG; 1758c792928fSAndrew Gallatin return m; 1759c792928fSAndrew Gallatin } 1760c792928fSAndrew Gallatin 1761aed8e389SAndrew Gallatin static void 17626d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1763b2fc195eSAndrew Gallatin { 1764b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1765b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1766b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1767b2fc195eSAndrew Gallatin struct ifnet *ifp; 17686d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1769b2fc195eSAndrew Gallatin struct ip *ip; 1770c792928fSAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag, ip_off; 1771aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1772aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1773b2fc195eSAndrew Gallatin 1774b2fc195eSAndrew Gallatin 1775b2fc195eSAndrew Gallatin 1776b2fc195eSAndrew Gallatin ifp = sc->ifp; 1777b2fc195eSAndrew Gallatin tx = &sc->tx; 1778b2fc195eSAndrew Gallatin 1779c792928fSAndrew Gallatin ip_off = sizeof (struct ether_header); 1780c792928fSAndrew Gallatin if (m->m_flags & M_VLANTAG) { 1781c792928fSAndrew Gallatin m = mxge_vlan_tag_insert(m); 1782c792928fSAndrew Gallatin if (__predict_false(m == NULL)) 1783c792928fSAndrew Gallatin goto drop; 1784c792928fSAndrew Gallatin ip_off += ETHER_VLAN_ENCAP_LEN; 1785c792928fSAndrew Gallatin } 1786c792928fSAndrew Gallatin 1787b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1788b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1789b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1790aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1791b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1792adae7080SAndrew Gallatin if (__predict_false(err == EFBIG)) { 1793b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1794b2fc195eSAndrew Gallatin to defrag */ 1795b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1796b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1797b2fc195eSAndrew Gallatin goto drop; 1798b2fc195eSAndrew Gallatin } 1799adae7080SAndrew Gallatin sc->tx_defrag++; 1800b2fc195eSAndrew Gallatin m = m_tmp; 1801b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1802b2fc195eSAndrew Gallatin tx->info[idx].map, 1803aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1804b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1805b2fc195eSAndrew Gallatin } 1806adae7080SAndrew Gallatin if (__predict_false(err != 0)) { 1807aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1808aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1809b2fc195eSAndrew Gallatin goto drop; 1810b2fc195eSAndrew Gallatin } 1811b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1812b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 18135e7d8541SAndrew Gallatin tx->info[idx].m = m; 1814b2fc195eSAndrew Gallatin 1815aed8e389SAndrew Gallatin 1816aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1817aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1818c792928fSAndrew Gallatin mxge_encap_tso(sc, m, cnt, ip_off); 1819aed8e389SAndrew Gallatin return; 1820aed8e389SAndrew Gallatin } 1821aed8e389SAndrew Gallatin 1822b2fc195eSAndrew Gallatin req = tx->req_list; 1823b2fc195eSAndrew Gallatin cksum_offset = 0; 18245e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 18255e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1826b2fc195eSAndrew Gallatin 1827b2fc195eSAndrew Gallatin /* checksum offloading? */ 1828b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1829aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1830aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1831c792928fSAndrew Gallatin if (__predict_false(m->m_len < ip_off + sizeof (*ip))) { 1832c792928fSAndrew Gallatin m_copydata(m, 0, ip_off + sizeof (*ip), 1833aed8e389SAndrew Gallatin sc->scratch); 1834c792928fSAndrew Gallatin ip = (struct ip *)(sc->scratch + ip_off); 1835aed8e389SAndrew Gallatin } else { 1836c792928fSAndrew Gallatin ip = (struct ip *)(mtod(m, char *) + ip_off); 1837aed8e389SAndrew Gallatin } 1838c792928fSAndrew Gallatin cksum_offset = ip_off + (ip->ip_hl << 2); 1839b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 18405e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1841b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 18425e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1843aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1844aed8e389SAndrew Gallatin } else { 1845aed8e389SAndrew Gallatin odd_flag = 0; 1846b2fc195eSAndrew Gallatin } 18475e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 18485e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1849b2fc195eSAndrew Gallatin 1850b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1851b2fc195eSAndrew Gallatin cum_len = 0; 1852aed8e389SAndrew Gallatin seg = tx->seg_list; 18535e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1854b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1855b2fc195eSAndrew Gallatin req->addr_low = 18566d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1857b2fc195eSAndrew Gallatin req->addr_high = 18586d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1859b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1860b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1861b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1862b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1863b2fc195eSAndrew Gallatin else 1864b2fc195eSAndrew Gallatin cksum_offset = 0; 18655e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 18665e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 18675e7d8541SAndrew Gallatin req->rdma_count = 1; 1868aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1869b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1870b2fc195eSAndrew Gallatin seg++; 1871b2fc195eSAndrew Gallatin req++; 1872b2fc195eSAndrew Gallatin req->flags = 0; 1873b2fc195eSAndrew Gallatin } 1874b2fc195eSAndrew Gallatin req--; 1875b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1876b2fc195eSAndrew Gallatin if (cum_len < 60) { 1877b2fc195eSAndrew Gallatin req++; 1878b2fc195eSAndrew Gallatin req->addr_low = 18796d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1880b2fc195eSAndrew Gallatin req->addr_high = 18816d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1882b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 18835e7d8541SAndrew Gallatin req->cksum_offset = 0; 18845e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 18855e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 18865e7d8541SAndrew Gallatin req->rdma_count = 1; 1887aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1888b2fc195eSAndrew Gallatin cnt++; 1889b2fc195eSAndrew Gallatin } 18905e7d8541SAndrew Gallatin 18915e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 18925e7d8541SAndrew Gallatin #if 0 18935e7d8541SAndrew Gallatin /* print what the firmware will see */ 18945e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 18955e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 18965e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 18975e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 18985e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 18995e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 19005e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 19015e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 19025e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 19035e7d8541SAndrew Gallatin } 19045e7d8541SAndrew Gallatin printf("--------------\n"); 19055e7d8541SAndrew Gallatin #endif 19065e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 19076d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1908b2fc195eSAndrew Gallatin return; 1909b2fc195eSAndrew Gallatin 1910b2fc195eSAndrew Gallatin drop: 1911b2fc195eSAndrew Gallatin m_freem(m); 1912b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1913b2fc195eSAndrew Gallatin return; 1914b2fc195eSAndrew Gallatin } 1915b2fc195eSAndrew Gallatin 1916b2fc195eSAndrew Gallatin 19176d914a32SAndrew Gallatin 19186d914a32SAndrew Gallatin 19196d914a32SAndrew Gallatin static inline void 19206d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1921b2fc195eSAndrew Gallatin { 1922b2fc195eSAndrew Gallatin struct mbuf *m; 1923b2fc195eSAndrew Gallatin struct ifnet *ifp; 1924adae7080SAndrew Gallatin mxge_tx_buf_t *tx; 1925b2fc195eSAndrew Gallatin 1926b2fc195eSAndrew Gallatin ifp = sc->ifp; 1927adae7080SAndrew Gallatin tx = &sc->tx; 1928adae7080SAndrew Gallatin while ((tx->mask - (tx->req - tx->done)) > tx->max_desc) { 19296d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 19306d914a32SAndrew Gallatin if (m == NULL) { 19316d914a32SAndrew Gallatin return; 19326d914a32SAndrew Gallatin } 1933b2fc195eSAndrew Gallatin /* let BPF see it */ 1934b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1935b2fc195eSAndrew Gallatin 1936b2fc195eSAndrew Gallatin /* give it to the nic */ 19376d87a65dSAndrew Gallatin mxge_encap(sc, m); 19386d914a32SAndrew Gallatin } 19396d914a32SAndrew Gallatin /* ran out of transmit slots */ 1940a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 1941b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1942adae7080SAndrew Gallatin tx->stall++; 1943a82c2581SAndrew Gallatin } 1944b2fc195eSAndrew Gallatin } 1945b2fc195eSAndrew Gallatin 1946b2fc195eSAndrew Gallatin static void 19476d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1948b2fc195eSAndrew Gallatin { 19496d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1950b2fc195eSAndrew Gallatin 1951b2fc195eSAndrew Gallatin 1952a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 19536d87a65dSAndrew Gallatin mxge_start_locked(sc); 1954a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 1955b2fc195eSAndrew Gallatin } 1956b2fc195eSAndrew Gallatin 19575e7d8541SAndrew Gallatin /* 19585e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 19595e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 19605e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 19615e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 19625e7d8541SAndrew Gallatin * in a burst 19635e7d8541SAndrew Gallatin */ 19645e7d8541SAndrew Gallatin static inline void 19655e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 19665e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 19675e7d8541SAndrew Gallatin { 19685e7d8541SAndrew Gallatin uint32_t low; 19695e7d8541SAndrew Gallatin 19705e7d8541SAndrew Gallatin low = src->addr_low; 19715e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 1972a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 1973a1480dfbSAndrew Gallatin mb(); 1974a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 19755e7d8541SAndrew Gallatin mb(); 197640385a5fSAndrew Gallatin src->addr_low = low; 19775e7d8541SAndrew Gallatin dst->addr_low = low; 19785e7d8541SAndrew Gallatin mb(); 19795e7d8541SAndrew Gallatin } 19805e7d8541SAndrew Gallatin 1981b2fc195eSAndrew Gallatin static int 19826d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1983b2fc195eSAndrew Gallatin { 1984b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1985b2fc195eSAndrew Gallatin struct mbuf *m; 19866d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1987b2fc195eSAndrew Gallatin int cnt, err; 1988b2fc195eSAndrew Gallatin 1989b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1990b2fc195eSAndrew Gallatin if (m == NULL) { 1991b2fc195eSAndrew Gallatin rx->alloc_fail++; 1992b2fc195eSAndrew Gallatin err = ENOBUFS; 1993b2fc195eSAndrew Gallatin goto done; 1994b2fc195eSAndrew Gallatin } 1995b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1996b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1997b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1998b2fc195eSAndrew Gallatin if (err != 0) { 1999b2fc195eSAndrew Gallatin m_free(m); 2000b2fc195eSAndrew Gallatin goto done; 2001b2fc195eSAndrew Gallatin } 2002b2fc195eSAndrew Gallatin rx->info[idx].m = m; 2003b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 20046d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 2005b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 20066d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 2007b2fc195eSAndrew Gallatin 2008b2fc195eSAndrew Gallatin done: 2009adae7080SAndrew Gallatin if ((idx & 7) == 7) 2010adae7080SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], &rx->shadow[idx - 7]); 2011b2fc195eSAndrew Gallatin return err; 2012b2fc195eSAndrew Gallatin } 2013b2fc195eSAndrew Gallatin 2014b2fc195eSAndrew Gallatin static int 20156d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 2016b2fc195eSAndrew Gallatin { 2017053e637fSAndrew Gallatin bus_dma_segment_t seg[3]; 2018b2fc195eSAndrew Gallatin struct mbuf *m; 20196d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 2020053e637fSAndrew Gallatin int cnt, err, i; 2021b2fc195eSAndrew Gallatin 2022a0394e33SAndrew Gallatin if (rx->cl_size == MCLBYTES) 2023a0394e33SAndrew Gallatin m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 2024a0394e33SAndrew Gallatin else 2025053e637fSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, rx->cl_size); 2026b2fc195eSAndrew Gallatin if (m == NULL) { 2027b2fc195eSAndrew Gallatin rx->alloc_fail++; 2028b2fc195eSAndrew Gallatin err = ENOBUFS; 2029b2fc195eSAndrew Gallatin goto done; 2030b2fc195eSAndrew Gallatin } 2031053e637fSAndrew Gallatin m->m_len = rx->cl_size; 2032b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 2033053e637fSAndrew Gallatin seg, &cnt, BUS_DMA_NOWAIT); 2034b2fc195eSAndrew Gallatin if (err != 0) { 2035b2fc195eSAndrew Gallatin m_free(m); 2036b2fc195eSAndrew Gallatin goto done; 2037b2fc195eSAndrew Gallatin } 2038b2fc195eSAndrew Gallatin rx->info[idx].m = m; 2039053e637fSAndrew Gallatin 2040053e637fSAndrew Gallatin for (i = 0; i < cnt; i++) { 2041053e637fSAndrew Gallatin rx->shadow[idx + i].addr_low = 2042053e637fSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg[i].ds_addr)); 2043053e637fSAndrew Gallatin rx->shadow[idx + i].addr_high = 2044053e637fSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg[i].ds_addr)); 2045053e637fSAndrew Gallatin } 2046053e637fSAndrew Gallatin 2047b2fc195eSAndrew Gallatin 2048b2fc195eSAndrew Gallatin done: 2049053e637fSAndrew Gallatin for (i = 0; i < rx->nbufs; i++) { 2050b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 20515e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 20525e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 2053b2fc195eSAndrew Gallatin } 2054053e637fSAndrew Gallatin idx++; 2055053e637fSAndrew Gallatin } 2056b2fc195eSAndrew Gallatin return err; 2057b2fc195eSAndrew Gallatin } 2058b2fc195eSAndrew Gallatin 20599b03b0f3SAndrew Gallatin /* 20609b03b0f3SAndrew Gallatin * Myri10GE hardware checksums are not valid if the sender 20619b03b0f3SAndrew Gallatin * padded the frame with non-zero padding. This is because 20629b03b0f3SAndrew Gallatin * the firmware just does a simple 16-bit 1s complement 20639b03b0f3SAndrew Gallatin * checksum across the entire frame, excluding the first 14 2064053e637fSAndrew Gallatin * bytes. It is best to simply to check the checksum and 2065053e637fSAndrew Gallatin * tell the stack about it only if the checksum is good 20669b03b0f3SAndrew Gallatin */ 20679b03b0f3SAndrew Gallatin 2068053e637fSAndrew Gallatin static inline uint16_t 2069053e637fSAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 2070053e637fSAndrew Gallatin { 2071053e637fSAndrew Gallatin struct ether_header *eh; 2072053e637fSAndrew Gallatin struct ip *ip; 2073053e637fSAndrew Gallatin uint16_t c; 2074053e637fSAndrew Gallatin 2075053e637fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2076053e637fSAndrew Gallatin 2077053e637fSAndrew Gallatin /* only deal with IPv4 TCP & UDP for now */ 2078053e637fSAndrew Gallatin if (__predict_false(eh->ether_type != htons(ETHERTYPE_IP))) 2079053e637fSAndrew Gallatin return 1; 2080053e637fSAndrew Gallatin ip = (struct ip *)(eh + 1); 2081053e637fSAndrew Gallatin if (__predict_false(ip->ip_p != IPPROTO_TCP && 2082053e637fSAndrew Gallatin ip->ip_p != IPPROTO_UDP)) 2083053e637fSAndrew Gallatin return 1; 2084053e637fSAndrew Gallatin 2085053e637fSAndrew Gallatin c = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 2086053e637fSAndrew Gallatin htonl(ntohs(csum) + ntohs(ip->ip_len) + 2087053e637fSAndrew Gallatin - (ip->ip_hl << 2) + ip->ip_p)); 2088053e637fSAndrew Gallatin c ^= 0xffff; 2089053e637fSAndrew Gallatin return (c); 20905e7d8541SAndrew Gallatin } 2091053e637fSAndrew Gallatin 2092c792928fSAndrew Gallatin static void 2093c792928fSAndrew Gallatin mxge_vlan_tag_remove(struct mbuf *m, uint32_t *csum) 2094c792928fSAndrew Gallatin { 2095c792928fSAndrew Gallatin struct ether_vlan_header *evl; 2096c792928fSAndrew Gallatin struct ether_header *eh; 2097c792928fSAndrew Gallatin uint32_t partial; 2098c792928fSAndrew Gallatin 2099c792928fSAndrew Gallatin evl = mtod(m, struct ether_vlan_header *); 2100c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2101c792928fSAndrew Gallatin 2102c792928fSAndrew Gallatin /* 2103c792928fSAndrew Gallatin * fix checksum by subtracting ETHER_VLAN_ENCAP_LEN bytes 2104c792928fSAndrew Gallatin * after what the firmware thought was the end of the ethernet 2105c792928fSAndrew Gallatin * header. 2106c792928fSAndrew Gallatin */ 2107c792928fSAndrew Gallatin 2108c792928fSAndrew Gallatin /* put checksum into host byte order */ 2109c792928fSAndrew Gallatin *csum = ntohs(*csum); 2110c792928fSAndrew Gallatin partial = ntohl(*(uint32_t *)(mtod(m, char *) + ETHER_HDR_LEN)); 2111c792928fSAndrew Gallatin (*csum) += ~partial; 2112c792928fSAndrew Gallatin (*csum) += ((*csum) < ~partial); 2113c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2114c792928fSAndrew Gallatin (*csum) = ((*csum) >> 16) + ((*csum) & 0xFFFF); 2115c792928fSAndrew Gallatin 2116c792928fSAndrew Gallatin /* restore checksum to network byte order; 2117c792928fSAndrew Gallatin later consumers expect this */ 2118c792928fSAndrew Gallatin *csum = htons(*csum); 2119c792928fSAndrew Gallatin 2120c792928fSAndrew Gallatin /* save the tag */ 2121c792928fSAndrew Gallatin m->m_flags |= M_VLANTAG; 2122c792928fSAndrew Gallatin m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); 2123c792928fSAndrew Gallatin 2124c792928fSAndrew Gallatin /* 2125c792928fSAndrew Gallatin * Remove the 802.1q header by copying the Ethernet 2126c792928fSAndrew Gallatin * addresses over it and adjusting the beginning of 2127c792928fSAndrew Gallatin * the data in the mbuf. The encapsulated Ethernet 2128c792928fSAndrew Gallatin * type field is already in place. 2129c792928fSAndrew Gallatin */ 2130c792928fSAndrew Gallatin bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, 2131c792928fSAndrew Gallatin ETHER_HDR_LEN - ETHER_TYPE_LEN); 2132c792928fSAndrew Gallatin m_adj(m, ETHER_VLAN_ENCAP_LEN); 2133c792928fSAndrew Gallatin } 2134c792928fSAndrew Gallatin 21355e7d8541SAndrew Gallatin 21365e7d8541SAndrew Gallatin static inline void 2137053e637fSAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2138b2fc195eSAndrew Gallatin { 2139b2fc195eSAndrew Gallatin struct ifnet *ifp; 2140053e637fSAndrew Gallatin struct mbuf *m; 2141c792928fSAndrew Gallatin struct ether_header *eh; 21426d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2143053e637fSAndrew Gallatin bus_dmamap_t old_map; 2144b2fc195eSAndrew Gallatin int idx; 2145053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2146b2fc195eSAndrew Gallatin 2147b2fc195eSAndrew Gallatin ifp = sc->ifp; 2148053e637fSAndrew Gallatin rx = &sc->rx_big; 2149b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2150053e637fSAndrew Gallatin rx->cnt += rx->nbufs; 2151b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2152b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2153b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 21546d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 2155053e637fSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2156053e637fSAndrew Gallatin ifp->if_ierrors++; 2157053e637fSAndrew Gallatin return; 2158b2fc195eSAndrew Gallatin } 2159053e637fSAndrew Gallatin 2160b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2161b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2162b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2163b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2164b2fc195eSAndrew Gallatin 2165b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2166b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2167b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2168b2fc195eSAndrew Gallatin 2169053e637fSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2170053e637fSAndrew Gallatin * aligned */ 21715e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2172b2fc195eSAndrew Gallatin 2173053e637fSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 2174053e637fSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 21759b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2176c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2177c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2178c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2179c792928fSAndrew Gallatin } 2180b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2181053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2182053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2183b2fc195eSAndrew Gallatin return; 2184053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2185053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2186053e637fSAndrew Gallatin checksum is good */ 2187053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2188053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2189b2fc195eSAndrew Gallatin } 2190053e637fSAndrew Gallatin /* pass the frame up the stack */ 2191053e637fSAndrew Gallatin (*ifp->if_input)(ifp, m); 2192b2fc195eSAndrew Gallatin } 2193b2fc195eSAndrew Gallatin 2194b2fc195eSAndrew Gallatin static inline void 21955e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 2196b2fc195eSAndrew Gallatin { 2197b2fc195eSAndrew Gallatin struct ifnet *ifp; 2198c792928fSAndrew Gallatin struct ether_header *eh; 2199b2fc195eSAndrew Gallatin struct mbuf *m; 22006d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 2201b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 2202b2fc195eSAndrew Gallatin int idx; 2203053e637fSAndrew Gallatin uint16_t tcpudp_csum; 2204b2fc195eSAndrew Gallatin 2205b2fc195eSAndrew Gallatin ifp = sc->ifp; 2206b2fc195eSAndrew Gallatin rx = &sc->rx_small; 2207b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 2208b2fc195eSAndrew Gallatin rx->cnt++; 2209b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 2210b2fc195eSAndrew Gallatin m = rx->info[idx].m; 2211b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 22126d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 2213b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 2214b2fc195eSAndrew Gallatin ifp->if_ierrors++; 2215b2fc195eSAndrew Gallatin return; 2216b2fc195eSAndrew Gallatin } 2217b2fc195eSAndrew Gallatin 2218b2fc195eSAndrew Gallatin /* unmap the received buffer */ 2219b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 2220b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 2221b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 2222b2fc195eSAndrew Gallatin 2223b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 2224b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 2225b2fc195eSAndrew Gallatin rx->extra_map = old_map; 2226b2fc195eSAndrew Gallatin 2227b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 2228b2fc195eSAndrew Gallatin * aligned */ 22295e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 2230b2fc195eSAndrew Gallatin 22319b03b0f3SAndrew Gallatin m->m_pkthdr.rcvif = ifp; 22329b03b0f3SAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 22339b03b0f3SAndrew Gallatin ifp->if_ipackets++; 2234c792928fSAndrew Gallatin eh = mtod(m, struct ether_header *); 2235c792928fSAndrew Gallatin if (eh->ether_type == htons(ETHERTYPE_VLAN)) { 2236c792928fSAndrew Gallatin mxge_vlan_tag_remove(m, &csum); 2237c792928fSAndrew Gallatin } 2238b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 2239053e637fSAndrew Gallatin if (sc->csum_flag && (0 == (tcpudp_csum = mxge_rx_csum(m, csum)))) { 2240053e637fSAndrew Gallatin if (sc->lro_cnt && (0 == mxge_lro_rx(sc, m, csum))) 2241053e637fSAndrew Gallatin return; 2242053e637fSAndrew Gallatin /* otherwise, it was a UDP frame, or a TCP frame which 2243053e637fSAndrew Gallatin we could not do LRO on. Tell the stack that the 2244053e637fSAndrew Gallatin checksum is good */ 2245053e637fSAndrew Gallatin m->m_pkthdr.csum_data = 0xffff; 2246053e637fSAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_PSEUDO_HDR | CSUM_DATA_VALID; 2247053e637fSAndrew Gallatin } 2248b2fc195eSAndrew Gallatin 2249b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 2250b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 2251b2fc195eSAndrew Gallatin } 2252b2fc195eSAndrew Gallatin 2253b2fc195eSAndrew Gallatin static inline void 22545e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 22555e7d8541SAndrew Gallatin { 22565e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 2257053e637fSAndrew Gallatin struct lro_entry *lro; 22585e7d8541SAndrew Gallatin int limit = 0; 22595e7d8541SAndrew Gallatin uint16_t length; 22605e7d8541SAndrew Gallatin uint16_t checksum; 22615e7d8541SAndrew Gallatin 22625e7d8541SAndrew Gallatin 22635e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 22645e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 22655e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 2266053e637fSAndrew Gallatin checksum = rx_done->entry[rx_done->idx].checksum; 2267b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 22685e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 22695e7d8541SAndrew Gallatin else 22705e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 22715e7d8541SAndrew Gallatin rx_done->cnt++; 2272adae7080SAndrew Gallatin rx_done->idx = rx_done->cnt & rx_done->mask; 22735e7d8541SAndrew Gallatin 22745e7d8541SAndrew Gallatin /* limit potential for livelock */ 2275f616ebc7SAndrew Gallatin if (__predict_false(++limit > rx_done->mask / 2)) 22765e7d8541SAndrew Gallatin break; 2277053e637fSAndrew Gallatin } 2278053e637fSAndrew Gallatin while(!SLIST_EMPTY(&sc->lro_active)) { 2279053e637fSAndrew Gallatin lro = SLIST_FIRST(&sc->lro_active); 2280053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_active, next); 2281053e637fSAndrew Gallatin mxge_lro_flush(sc, lro); 22825e7d8541SAndrew Gallatin } 22835e7d8541SAndrew Gallatin } 22845e7d8541SAndrew Gallatin 22855e7d8541SAndrew Gallatin 22865e7d8541SAndrew Gallatin static inline void 22876d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 2288b2fc195eSAndrew Gallatin { 2289b2fc195eSAndrew Gallatin struct ifnet *ifp; 22906d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 2291b2fc195eSAndrew Gallatin struct mbuf *m; 2292b2fc195eSAndrew Gallatin bus_dmamap_t map; 2293f616ebc7SAndrew Gallatin int idx; 2294b2fc195eSAndrew Gallatin 2295b2fc195eSAndrew Gallatin tx = &sc->tx; 2296b2fc195eSAndrew Gallatin ifp = sc->ifp; 22975e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2298b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2299b2fc195eSAndrew Gallatin tx->done++; 2300b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2301b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2302b2fc195eSAndrew Gallatin segment per-mbuf */ 2303b2fc195eSAndrew Gallatin if (m != NULL) { 2304b2fc195eSAndrew Gallatin ifp->if_opackets++; 2305b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2306b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2307b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2308b2fc195eSAndrew Gallatin m_freem(m); 2309b2fc195eSAndrew Gallatin } 23105e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 23115e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 23125e7d8541SAndrew Gallatin tx->pkt_done++; 23135e7d8541SAndrew Gallatin } 2314b2fc195eSAndrew Gallatin } 2315b2fc195eSAndrew Gallatin 2316b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2317b2fc195eSAndrew Gallatin its OK to send packets */ 2318b2fc195eSAndrew Gallatin 2319b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2320b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2321a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 2322b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2323a82c2581SAndrew Gallatin sc->tx.wake++; 23246d87a65dSAndrew Gallatin mxge_start_locked(sc); 2325a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 2326b2fc195eSAndrew Gallatin } 2327b2fc195eSAndrew Gallatin } 2328b2fc195eSAndrew Gallatin 2329b2fc195eSAndrew Gallatin static void 23306d87a65dSAndrew Gallatin mxge_intr(void *arg) 2331b2fc195eSAndrew Gallatin { 23326d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 23335e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 23345e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 23355e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 23365e7d8541SAndrew Gallatin uint32_t send_done_count; 23375e7d8541SAndrew Gallatin uint8_t valid; 2338b2fc195eSAndrew Gallatin 2339b2fc195eSAndrew Gallatin 23405e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 23415e7d8541SAndrew Gallatin if (!stats->valid) { 23425e7d8541SAndrew Gallatin return; 2343b2fc195eSAndrew Gallatin } 23445e7d8541SAndrew Gallatin valid = stats->valid; 2345b2fc195eSAndrew Gallatin 2346dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 23475e7d8541SAndrew Gallatin /* lower legacy IRQ */ 23485e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 23495e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 23505e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 23515e7d8541SAndrew Gallatin stats->valid = 0; 2352dc8731d4SAndrew Gallatin } else { 2353dc8731d4SAndrew Gallatin stats->valid = 0; 2354dc8731d4SAndrew Gallatin } 2355dc8731d4SAndrew Gallatin 2356dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 23575e7d8541SAndrew Gallatin do { 23585e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 23595e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 23605e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 23615e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 23625e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 23635e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 23645e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2365b2fc195eSAndrew Gallatin } 23665e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2367b2fc195eSAndrew Gallatin 23685e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 23695e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 23705e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2371b2fc195eSAndrew Gallatin if (sc->link_state) { 23725e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 23735e7d8541SAndrew Gallatin if (mxge_verbose) 23745e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2375b2fc195eSAndrew Gallatin } else { 23765e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 23775e7d8541SAndrew Gallatin if (mxge_verbose) 23785e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2379b2fc195eSAndrew Gallatin } 2380b2fc195eSAndrew Gallatin } 2381b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2382b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2383b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2384b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 23855e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 23865e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 23875e7d8541SAndrew Gallatin } 23885e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2389b2fc195eSAndrew Gallatin } 2390b2fc195eSAndrew Gallatin 23915e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 23925e7d8541SAndrew Gallatin if (valid & 0x1) 23935e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 23945e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2395b2fc195eSAndrew Gallatin } 2396b2fc195eSAndrew Gallatin 2397b2fc195eSAndrew Gallatin static void 23986d87a65dSAndrew Gallatin mxge_init(void *arg) 2399b2fc195eSAndrew Gallatin { 2400b2fc195eSAndrew Gallatin } 2401b2fc195eSAndrew Gallatin 2402b2fc195eSAndrew Gallatin 2403b2fc195eSAndrew Gallatin 2404b2fc195eSAndrew Gallatin static void 24056d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2406b2fc195eSAndrew Gallatin { 2407b2fc195eSAndrew Gallatin int i; 2408b2fc195eSAndrew Gallatin 2409b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2410b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2411b2fc195eSAndrew Gallatin continue; 2412b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2413b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2414b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2415b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2416b2fc195eSAndrew Gallatin } 2417b2fc195eSAndrew Gallatin 24189b03b0f3SAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 24199b03b0f3SAndrew Gallatin if (sc->rx_small.info[i].m == NULL) 2420b2fc195eSAndrew Gallatin continue; 24219b03b0f3SAndrew Gallatin bus_dmamap_unload(sc->rx_small.dmat, 24229b03b0f3SAndrew Gallatin sc->rx_small.info[i].map); 24239b03b0f3SAndrew Gallatin m_freem(sc->rx_small.info[i].m); 24249b03b0f3SAndrew Gallatin sc->rx_small.info[i].m = NULL; 2425b2fc195eSAndrew Gallatin } 2426b2fc195eSAndrew Gallatin 2427b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2428dce01b9bSAndrew Gallatin sc->tx.info[i].flag = 0; 2429b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2430b2fc195eSAndrew Gallatin continue; 2431b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2432b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2433b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2434b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2435b2fc195eSAndrew Gallatin } 2436b2fc195eSAndrew Gallatin } 2437b2fc195eSAndrew Gallatin 2438b2fc195eSAndrew Gallatin static void 24396d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2440b2fc195eSAndrew Gallatin { 2441b2fc195eSAndrew Gallatin int i; 2442b2fc195eSAndrew Gallatin 2443adae7080SAndrew Gallatin if (sc->rx_done.entry != NULL) 2444adae7080SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 2445adae7080SAndrew Gallatin sc->rx_done.entry = NULL; 2446aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2447b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2448aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2449aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2450b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2451b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2452b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2453b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2454b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2455c2657176SAndrew Gallatin if (sc->tx.dmat != NULL) { 2456b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2457b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2458b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2459b2fc195eSAndrew Gallatin } 2460c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2461c2657176SAndrew Gallatin } 2462b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2463b2fc195eSAndrew Gallatin } 2464b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2465c2657176SAndrew Gallatin if (sc->rx_small.dmat != NULL) { 2466b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2467b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2468b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2469b2fc195eSAndrew Gallatin } 2470c2657176SAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2471c2657176SAndrew Gallatin sc->rx_small.extra_map); 2472c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2473c2657176SAndrew Gallatin } 2474b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2475b2fc195eSAndrew Gallatin } 2476b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2477c2657176SAndrew Gallatin if (sc->rx_big.dmat != NULL) { 2478b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2479b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2480b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2481b2fc195eSAndrew Gallatin } 2482b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2483b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2484b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2485b2fc195eSAndrew Gallatin } 2486c2657176SAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2487c2657176SAndrew Gallatin } 2488c2657176SAndrew Gallatin } 2489b2fc195eSAndrew Gallatin 2490b2fc195eSAndrew Gallatin static int 24916d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2492b2fc195eSAndrew Gallatin { 24936d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2494b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2495b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2496b2fc195eSAndrew Gallatin int i, err; 2497b2fc195eSAndrew Gallatin unsigned long bytes; 2498b2fc195eSAndrew Gallatin 2499b2fc195eSAndrew Gallatin /* get ring sizes */ 25005e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2501b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 25025e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2503b2fc195eSAndrew Gallatin if (err != 0) { 2504b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2505b2fc195eSAndrew Gallatin goto abort_with_nothing; 2506b2fc195eSAndrew Gallatin } 2507b2fc195eSAndrew Gallatin 2508b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2509b2fc195eSAndrew Gallatin 2510b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2511b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 251276bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 2513a82c2581SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 251476bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2515b2fc195eSAndrew Gallatin 2516b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2517adae7080SAndrew Gallatin sc->tx.max_desc = MIN(MXGE_MAX_SEND_DESC, tx_ring_entries / 4); 2518b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2519adae7080SAndrew Gallatin sc->rx_done.mask = (2 * rx_ring_entries) - 1; 2520b2fc195eSAndrew Gallatin 2521b2fc195eSAndrew Gallatin err = ENOMEM; 2522b2fc195eSAndrew Gallatin 2523adae7080SAndrew Gallatin /* allocate interrupt queues */ 2524adae7080SAndrew Gallatin bytes = (sc->rx_done.mask + 1) * sizeof (*sc->rx_done.entry); 2525adae7080SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2526adae7080SAndrew Gallatin if (err != 0) 2527adae7080SAndrew Gallatin goto abort_with_nothing; 2528adae7080SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 2529adae7080SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2530adae7080SAndrew Gallatin 2531b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2532b2fc195eSAndrew Gallatin bytes = 8 + 2533adae7080SAndrew Gallatin sizeof (*sc->tx.req_list) * (sc->tx.max_desc + 4); 2534b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2535b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2536adae7080SAndrew Gallatin goto abort_with_alloc; 2537b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2538b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2539b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2540b2fc195eSAndrew Gallatin 2541aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2542adae7080SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * sc->tx.max_desc; 2543aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2544aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2545aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2546aed8e389SAndrew Gallatin goto abort_with_alloc; 2547aed8e389SAndrew Gallatin 2548b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2549b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2550b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2551b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2552b2fc195eSAndrew Gallatin goto abort_with_alloc; 2553b2fc195eSAndrew Gallatin 2554b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2555b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2556b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2557b2fc195eSAndrew Gallatin goto abort_with_alloc; 2558b2fc195eSAndrew Gallatin 2559b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2560b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2561b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2562b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2563b2fc195eSAndrew Gallatin goto abort_with_alloc; 2564b2fc195eSAndrew Gallatin 2565b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2566b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2567b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2568b2fc195eSAndrew Gallatin goto abort_with_alloc; 2569b2fc195eSAndrew Gallatin 2570b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2571b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2572b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2573b2fc195eSAndrew Gallatin goto abort_with_alloc; 2574b2fc195eSAndrew Gallatin 2575b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2576b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2577b2fc195eSAndrew Gallatin 1, /* alignment */ 2578b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2579b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2580b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2581b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2582aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2583adae7080SAndrew Gallatin sc->tx.max_desc - 2, /* num segs */ 2584b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2585b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2586b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2587b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2588b2fc195eSAndrew Gallatin 2589b2fc195eSAndrew Gallatin if (err != 0) { 2590b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2591b2fc195eSAndrew Gallatin err); 2592b2fc195eSAndrew Gallatin goto abort_with_alloc; 2593b2fc195eSAndrew Gallatin } 2594b2fc195eSAndrew Gallatin 2595b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2596b2fc195eSAndrew Gallatin 1, /* alignment */ 2597b2fc195eSAndrew Gallatin 4096, /* boundary */ 2598b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2599b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2600b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2601b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2602b2fc195eSAndrew Gallatin 1, /* num segs */ 2603b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2604b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2605b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2606b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2607b2fc195eSAndrew Gallatin if (err != 0) { 2608b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2609b2fc195eSAndrew Gallatin err); 2610b2fc195eSAndrew Gallatin goto abort_with_alloc; 2611b2fc195eSAndrew Gallatin } 2612b2fc195eSAndrew Gallatin 2613b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2614b2fc195eSAndrew Gallatin 1, /* alignment */ 2615b2fc195eSAndrew Gallatin 4096, /* boundary */ 2616b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2617b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2618b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2619053e637fSAndrew Gallatin 3*4096, /* maxsize */ 2620053e637fSAndrew Gallatin 3, /* num segs */ 2621b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2622b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2623b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2624b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2625b2fc195eSAndrew Gallatin if (err != 0) { 2626b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2627b2fc195eSAndrew Gallatin err); 2628b2fc195eSAndrew Gallatin goto abort_with_alloc; 2629b2fc195eSAndrew Gallatin } 2630b2fc195eSAndrew Gallatin 2631b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2632b2fc195eSAndrew Gallatin in each ring */ 2633b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2634b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2635b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2636b2fc195eSAndrew Gallatin if (err != 0) { 2637b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2638b2fc195eSAndrew Gallatin err); 2639b2fc195eSAndrew Gallatin goto abort_with_alloc; 2640b2fc195eSAndrew Gallatin } 2641b2fc195eSAndrew Gallatin } 2642b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2643b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2644b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2645b2fc195eSAndrew Gallatin if (err != 0) { 2646b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2647b2fc195eSAndrew Gallatin err); 2648b2fc195eSAndrew Gallatin goto abort_with_alloc; 2649b2fc195eSAndrew Gallatin } 2650b2fc195eSAndrew Gallatin } 2651b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2652b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2653b2fc195eSAndrew Gallatin if (err != 0) { 2654b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2655b2fc195eSAndrew Gallatin err); 2656b2fc195eSAndrew Gallatin goto abort_with_alloc; 2657b2fc195eSAndrew Gallatin } 2658b2fc195eSAndrew Gallatin 2659b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2660b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2661b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2662b2fc195eSAndrew Gallatin if (err != 0) { 2663b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2664b2fc195eSAndrew Gallatin err); 2665b2fc195eSAndrew Gallatin goto abort_with_alloc; 2666b2fc195eSAndrew Gallatin } 2667b2fc195eSAndrew Gallatin } 2668b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2669b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2670b2fc195eSAndrew Gallatin if (err != 0) { 2671b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2672b2fc195eSAndrew Gallatin err); 2673b2fc195eSAndrew Gallatin goto abort_with_alloc; 2674b2fc195eSAndrew Gallatin } 2675b2fc195eSAndrew Gallatin return 0; 2676b2fc195eSAndrew Gallatin 2677b2fc195eSAndrew Gallatin abort_with_alloc: 26786d87a65dSAndrew Gallatin mxge_free_rings(sc); 2679b2fc195eSAndrew Gallatin 2680b2fc195eSAndrew Gallatin abort_with_nothing: 2681b2fc195eSAndrew Gallatin return err; 2682b2fc195eSAndrew Gallatin } 2683b2fc195eSAndrew Gallatin 2684053e637fSAndrew Gallatin static void 2685053e637fSAndrew Gallatin mxge_choose_params(int mtu, int *big_buf_size, int *cl_size, int *nbufs) 2686053e637fSAndrew Gallatin { 2687c792928fSAndrew Gallatin int bufsize = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + MXGEFW_PAD; 2688053e637fSAndrew Gallatin 2689053e637fSAndrew Gallatin if (bufsize < MCLBYTES) { 2690053e637fSAndrew Gallatin /* easy, everything fits in a single buffer */ 2691053e637fSAndrew Gallatin *big_buf_size = MCLBYTES; 2692053e637fSAndrew Gallatin *cl_size = MCLBYTES; 2693053e637fSAndrew Gallatin *nbufs = 1; 2694053e637fSAndrew Gallatin return; 2695053e637fSAndrew Gallatin } 2696053e637fSAndrew Gallatin 2697053e637fSAndrew Gallatin if (bufsize < MJUMPAGESIZE) { 2698053e637fSAndrew Gallatin /* still easy, everything still fits in a single buffer */ 2699053e637fSAndrew Gallatin *big_buf_size = MJUMPAGESIZE; 2700053e637fSAndrew Gallatin *cl_size = MJUMPAGESIZE; 2701053e637fSAndrew Gallatin *nbufs = 1; 2702053e637fSAndrew Gallatin return; 2703053e637fSAndrew Gallatin } 2704053e637fSAndrew Gallatin /* now we need to use virtually contiguous buffers */ 2705053e637fSAndrew Gallatin *cl_size = MJUM9BYTES; 2706053e637fSAndrew Gallatin *big_buf_size = 4096; 2707053e637fSAndrew Gallatin *nbufs = mtu / 4096 + 1; 2708053e637fSAndrew Gallatin /* needs to be a power of two, so round up */ 2709053e637fSAndrew Gallatin if (*nbufs == 3) 2710053e637fSAndrew Gallatin *nbufs = 4; 2711053e637fSAndrew Gallatin } 2712053e637fSAndrew Gallatin 2713b2fc195eSAndrew Gallatin static int 27146d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2715b2fc195eSAndrew Gallatin { 27166d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2717053e637fSAndrew Gallatin int i, err, big_bytes; 2718b2fc195eSAndrew Gallatin bus_dmamap_t map; 27190fa7f681SAndrew Gallatin bus_addr_t bus; 2720053e637fSAndrew Gallatin struct lro_entry *lro_entry; 2721b2fc195eSAndrew Gallatin 2722053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_free); 2723053e637fSAndrew Gallatin SLIST_INIT(&sc->lro_active); 2724053e637fSAndrew Gallatin 2725053e637fSAndrew Gallatin for (i = 0; i < sc->lro_cnt; i++) { 2726053e637fSAndrew Gallatin lro_entry = (struct lro_entry *) 2727053e637fSAndrew Gallatin malloc(sizeof (*lro_entry), M_DEVBUF, M_NOWAIT | M_ZERO); 2728053e637fSAndrew Gallatin if (lro_entry == NULL) { 2729053e637fSAndrew Gallatin sc->lro_cnt = i; 2730053e637fSAndrew Gallatin break; 2731053e637fSAndrew Gallatin } 2732053e637fSAndrew Gallatin SLIST_INSERT_HEAD(&sc->lro_free, lro_entry, next); 2733053e637fSAndrew Gallatin } 2734b2fc195eSAndrew Gallatin 27357d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 27367d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 27377d542e2dSAndrew Gallatin 2738adae7080SAndrew Gallatin err = mxge_reset(sc, 1); 2739b2fc195eSAndrew Gallatin if (err != 0) { 2740b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2741b2fc195eSAndrew Gallatin return EIO; 2742b2fc195eSAndrew Gallatin } 2743b2fc195eSAndrew Gallatin 2744053e637fSAndrew Gallatin mxge_choose_params(sc->ifp->if_mtu, &big_bytes, 2745053e637fSAndrew Gallatin &sc->rx_big.cl_size, &sc->rx_big.nbufs); 2746b2fc195eSAndrew Gallatin 2747053e637fSAndrew Gallatin cmd.data0 = sc->rx_big.nbufs; 2748053e637fSAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS, 2749053e637fSAndrew Gallatin &cmd); 2750053e637fSAndrew Gallatin /* error is only meaningful if we're trying to set 2751053e637fSAndrew Gallatin MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS > 1 */ 2752053e637fSAndrew Gallatin if (err && sc->rx_big.nbufs > 1) { 2753053e637fSAndrew Gallatin device_printf(sc->dev, 2754053e637fSAndrew Gallatin "Failed to set alway-use-n to %d\n", 2755053e637fSAndrew Gallatin sc->rx_big.nbufs); 2756053e637fSAndrew Gallatin return EIO; 2757053e637fSAndrew Gallatin } 2758b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2759b2fc195eSAndrew Gallatin 27605e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2761b2fc195eSAndrew Gallatin sc->tx.lanai = 2762b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 27636d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 27645e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2765b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2766b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 27675e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2768b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2769b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2770b2fc195eSAndrew Gallatin 2771b2fc195eSAndrew Gallatin if (err != 0) { 2772b2fc195eSAndrew Gallatin device_printf(sc->dev, 2773b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2774a98d6cd7SAndrew Gallatin return EIO; 2775b2fc195eSAndrew Gallatin } 2776b2fc195eSAndrew Gallatin 2777b2fc195eSAndrew Gallatin /* stock receive rings */ 2778b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2779b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 27806d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2781b2fc195eSAndrew Gallatin if (err) { 2782b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2783b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2784b2fc195eSAndrew Gallatin goto abort; 2785b2fc195eSAndrew Gallatin } 2786b2fc195eSAndrew Gallatin } 2787b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2788053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_low = 0xffffffff; 2789053e637fSAndrew Gallatin sc->rx_big.shadow[i].addr_high = 0xffffffff; 2790053e637fSAndrew Gallatin } 2791053e637fSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i += sc->rx_big.nbufs) { 2792b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 27936d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2794b2fc195eSAndrew Gallatin if (err) { 2795b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2796b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2797b2fc195eSAndrew Gallatin goto abort; 2798b2fc195eSAndrew Gallatin } 2799b2fc195eSAndrew Gallatin } 2800b2fc195eSAndrew Gallatin 2801b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2802b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2803b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2804c792928fSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 28055e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2806b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 28075e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2808b2fc195eSAndrew Gallatin &cmd); 2809053e637fSAndrew Gallatin cmd.data0 = big_bytes; 28105e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 28110fa7f681SAndrew Gallatin 28120fa7f681SAndrew Gallatin if (err != 0) { 28130fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 28140fa7f681SAndrew Gallatin goto abort; 28150fa7f681SAndrew Gallatin } 28160fa7f681SAndrew Gallatin 2817b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 28186d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 28196d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 28200fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 28210fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 28220fa7f681SAndrew Gallatin 28230fa7f681SAndrew Gallatin if (err != 0) { 28240fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 28250fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 28260fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 28270fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 28280fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 28290fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 28300fa7f681SAndrew Gallatin &cmd); 28310fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 28320fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 28330fa7f681SAndrew Gallatin } else { 28340fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 28350fa7f681SAndrew Gallatin } 2836b2fc195eSAndrew Gallatin 2837b2fc195eSAndrew Gallatin if (err != 0) { 2838b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2839b2fc195eSAndrew Gallatin goto abort; 2840b2fc195eSAndrew Gallatin } 2841b2fc195eSAndrew Gallatin 2842b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 28435e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2844b2fc195eSAndrew Gallatin if (err) { 2845b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2846b2fc195eSAndrew Gallatin goto abort; 2847b2fc195eSAndrew Gallatin } 2848b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2849b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2850b2fc195eSAndrew Gallatin 2851b2fc195eSAndrew Gallatin return 0; 2852b2fc195eSAndrew Gallatin 2853b2fc195eSAndrew Gallatin 2854b2fc195eSAndrew Gallatin abort: 28556d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2856a98d6cd7SAndrew Gallatin 2857b2fc195eSAndrew Gallatin return err; 2858b2fc195eSAndrew Gallatin } 2859b2fc195eSAndrew Gallatin 2860b2fc195eSAndrew Gallatin static int 28616d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2862b2fc195eSAndrew Gallatin { 2863053e637fSAndrew Gallatin struct lro_entry *lro_entry; 28646d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2865b2fc195eSAndrew Gallatin int err, old_down_cnt; 2866b2fc195eSAndrew Gallatin 2867b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2868b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2869b2fc195eSAndrew Gallatin mb(); 28705e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2871b2fc195eSAndrew Gallatin if (err) { 2872b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2873b2fc195eSAndrew Gallatin } 2874b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2875b2fc195eSAndrew Gallatin /* wait for down irq */ 2876dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 2877b2fc195eSAndrew Gallatin } 2878b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2879b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2880b2fc195eSAndrew Gallatin } 2881a98d6cd7SAndrew Gallatin 28826d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2883a98d6cd7SAndrew Gallatin 2884053e637fSAndrew Gallatin while (!SLIST_EMPTY(&sc->lro_free)) { 2885053e637fSAndrew Gallatin lro_entry = SLIST_FIRST(&sc->lro_free); 2886053e637fSAndrew Gallatin SLIST_REMOVE_HEAD(&sc->lro_free, next); 2887053e637fSAndrew Gallatin } 2888b2fc195eSAndrew Gallatin return 0; 2889b2fc195eSAndrew Gallatin } 2890b2fc195eSAndrew Gallatin 2891dce01b9bSAndrew Gallatin static void 2892dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 2893dce01b9bSAndrew Gallatin { 2894dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2895dce01b9bSAndrew Gallatin int reg; 2896dce01b9bSAndrew Gallatin uint16_t cmd, lnk, pectl; 2897dce01b9bSAndrew Gallatin 2898dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2899dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2900dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2901dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2902dce01b9bSAndrew Gallatin 2903dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2904dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2905dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 2906dce01b9bSAndrew Gallatin } 2907dce01b9bSAndrew Gallatin 2908dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 2909dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 2910dce01b9bSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2911dce01b9bSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2912dce01b9bSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2913dce01b9bSAndrew Gallatin } 2914dce01b9bSAndrew Gallatin 2915dce01b9bSAndrew Gallatin static uint32_t 2916dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 2917dce01b9bSAndrew Gallatin { 2918dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2919dce01b9bSAndrew Gallatin uint32_t vs; 2920dce01b9bSAndrew Gallatin 2921dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 2922dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { 2923dce01b9bSAndrew Gallatin device_printf(sc->dev, 2924dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 2925dce01b9bSAndrew Gallatin return (uint32_t)-1; 2926dce01b9bSAndrew Gallatin } 2927dce01b9bSAndrew Gallatin /* enable read32 mode */ 2928dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 2929dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 2930dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 2931dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 2932dce01b9bSAndrew Gallatin } 2933dce01b9bSAndrew Gallatin 2934dce01b9bSAndrew Gallatin static void 2935dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 2936dce01b9bSAndrew Gallatin { 2937dce01b9bSAndrew Gallatin int err; 2938dce01b9bSAndrew Gallatin uint32_t reboot; 2939dce01b9bSAndrew Gallatin uint16_t cmd; 2940dce01b9bSAndrew Gallatin 2941dce01b9bSAndrew Gallatin err = ENXIO; 2942dce01b9bSAndrew Gallatin 2943dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 2944dce01b9bSAndrew Gallatin 2945dce01b9bSAndrew Gallatin /* 2946dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 2947dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 2948dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 2949dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 2950dce01b9bSAndrew Gallatin * again 2951dce01b9bSAndrew Gallatin */ 2952dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2953dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2954dce01b9bSAndrew Gallatin /* 2955dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 2956dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 2957dce01b9bSAndrew Gallatin * back, then give up 2958dce01b9bSAndrew Gallatin */ 2959dce01b9bSAndrew Gallatin DELAY(1000*100); 2960dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2961dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2962dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 2963dce01b9bSAndrew Gallatin goto abort; 2964dce01b9bSAndrew Gallatin } 2965dce01b9bSAndrew Gallatin } 2966dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 2967dce01b9bSAndrew Gallatin /* print the reboot status */ 2968dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 2969dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 2970dce01b9bSAndrew Gallatin reboot); 2971dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 2972dce01b9bSAndrew Gallatin 2973dce01b9bSAndrew Gallatin /* XXXX waiting for pci_cfg_restore() to be exported */ 2974dce01b9bSAndrew Gallatin goto abort; /* just abort for now */ 2975dce01b9bSAndrew Gallatin 2976dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 2977dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2978dce01b9bSAndrew Gallatin } else { 2979dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC did not reboot, ring state:\n"); 2980dce01b9bSAndrew Gallatin device_printf(sc->dev, "tx.req=%d tx.done=%d\n", 2981dce01b9bSAndrew Gallatin sc->tx.req, sc->tx.done); 2982dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 2983dce01b9bSAndrew Gallatin sc->tx.pkt_done, 2984dce01b9bSAndrew Gallatin be32toh(sc->fw_stats->send_done_count)); 2985dce01b9bSAndrew Gallatin } 2986dce01b9bSAndrew Gallatin 2987dce01b9bSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { 2988dce01b9bSAndrew Gallatin mxge_close(sc); 2989dce01b9bSAndrew Gallatin err = mxge_open(sc); 2990dce01b9bSAndrew Gallatin } 2991dce01b9bSAndrew Gallatin 2992dce01b9bSAndrew Gallatin abort: 2993dce01b9bSAndrew Gallatin /* 2994dce01b9bSAndrew Gallatin * stop the watchdog if the nic is dead, to avoid spamming the 2995dce01b9bSAndrew Gallatin * console 2996dce01b9bSAndrew Gallatin */ 2997dce01b9bSAndrew Gallatin if (err != 0) { 2998dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2999dce01b9bSAndrew Gallatin } 3000dce01b9bSAndrew Gallatin } 3001dce01b9bSAndrew Gallatin 3002dce01b9bSAndrew Gallatin static void 3003dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 3004dce01b9bSAndrew Gallatin { 3005dce01b9bSAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 3006dce01b9bSAndrew Gallatin 3007dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 3008dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 3009dce01b9bSAndrew Gallatin if (tx->req != tx->done && 3010dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 3011dce01b9bSAndrew Gallatin tx->done == tx->watchdog_done) 3012dce01b9bSAndrew Gallatin mxge_watchdog_reset(sc); 3013dce01b9bSAndrew Gallatin 3014dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 3015dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 3016dce01b9bSAndrew Gallatin } 3017dce01b9bSAndrew Gallatin 3018dce01b9bSAndrew Gallatin static void 3019dce01b9bSAndrew Gallatin mxge_tick(void *arg) 3020dce01b9bSAndrew Gallatin { 3021dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 3022dce01b9bSAndrew Gallatin 3023dce01b9bSAndrew Gallatin 3024dce01b9bSAndrew Gallatin /* Synchronize with possible callout reset/stop. */ 3025dce01b9bSAndrew Gallatin if (callout_pending(&sc->co_hdl) || 3026dce01b9bSAndrew Gallatin !callout_active(&sc->co_hdl)) { 3027dce01b9bSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3028dce01b9bSAndrew Gallatin return; 3029dce01b9bSAndrew Gallatin } 3030dce01b9bSAndrew Gallatin 3031dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 3032dce01b9bSAndrew Gallatin mxge_watchdog(sc); 3033dce01b9bSAndrew Gallatin } 3034b2fc195eSAndrew Gallatin 3035b2fc195eSAndrew Gallatin static int 30366d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 3037b2fc195eSAndrew Gallatin { 3038b2fc195eSAndrew Gallatin return EINVAL; 3039b2fc195eSAndrew Gallatin } 3040b2fc195eSAndrew Gallatin 3041b2fc195eSAndrew Gallatin static int 30426d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 3043b2fc195eSAndrew Gallatin { 3044b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 3045b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 3046b2fc195eSAndrew Gallatin int err = 0; 3047b2fc195eSAndrew Gallatin 3048b2fc195eSAndrew Gallatin 3049c792928fSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 3050053e637fSAndrew Gallatin if ((real_mtu > sc->max_mtu) || real_mtu < 60) 3051b2fc195eSAndrew Gallatin return EINVAL; 3052a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3053b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 3054b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 3055b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 3056dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 30576d87a65dSAndrew Gallatin mxge_close(sc); 30586d87a65dSAndrew Gallatin err = mxge_open(sc); 3059b2fc195eSAndrew Gallatin if (err != 0) { 3060b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 30616d87a65dSAndrew Gallatin mxge_close(sc); 30626d87a65dSAndrew Gallatin (void) mxge_open(sc); 3063b2fc195eSAndrew Gallatin } 3064dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 3065b2fc195eSAndrew Gallatin } 3066a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3067b2fc195eSAndrew Gallatin return err; 3068b2fc195eSAndrew Gallatin } 3069b2fc195eSAndrew Gallatin 3070b2fc195eSAndrew Gallatin static void 30716d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 3072b2fc195eSAndrew Gallatin { 30736d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 3074b2fc195eSAndrew Gallatin 3075b2fc195eSAndrew Gallatin 3076b2fc195eSAndrew Gallatin if (sc == NULL) 3077b2fc195eSAndrew Gallatin return; 3078b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 3079b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 3080b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 3081b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 3082b2fc195eSAndrew Gallatin } 3083b2fc195eSAndrew Gallatin 3084b2fc195eSAndrew Gallatin static int 30856d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 3086b2fc195eSAndrew Gallatin { 30876d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 3088b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 3089b2fc195eSAndrew Gallatin int err, mask; 3090b2fc195eSAndrew Gallatin 3091b2fc195eSAndrew Gallatin err = 0; 3092b2fc195eSAndrew Gallatin switch (command) { 3093b2fc195eSAndrew Gallatin case SIOCSIFADDR: 3094b2fc195eSAndrew Gallatin case SIOCGIFADDR: 3095b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 3096b2fc195eSAndrew Gallatin break; 3097b2fc195eSAndrew Gallatin 3098b2fc195eSAndrew Gallatin case SIOCSIFMTU: 30996d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 3100b2fc195eSAndrew Gallatin break; 3101b2fc195eSAndrew Gallatin 3102b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 3103a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3104b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 3105dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 31066d87a65dSAndrew Gallatin err = mxge_open(sc); 3107dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, 3108dce01b9bSAndrew Gallatin mxge_tick, sc); 3109dce01b9bSAndrew Gallatin } else { 31100fa7f681SAndrew Gallatin /* take care of promis can allmulti 31110fa7f681SAndrew Gallatin flag chages */ 31120fa7f681SAndrew Gallatin mxge_change_promisc(sc, 31130fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 31140fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 31150fa7f681SAndrew Gallatin } 3116b2fc195eSAndrew Gallatin } else { 3117dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 31186d87a65dSAndrew Gallatin mxge_close(sc); 3119dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3120dce01b9bSAndrew Gallatin } 3121b2fc195eSAndrew Gallatin } 3122a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3123b2fc195eSAndrew Gallatin break; 3124b2fc195eSAndrew Gallatin 3125b2fc195eSAndrew Gallatin case SIOCADDMULTI: 3126b2fc195eSAndrew Gallatin case SIOCDELMULTI: 3127a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 31280fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 3129a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3130b2fc195eSAndrew Gallatin break; 3131b2fc195eSAndrew Gallatin 3132b2fc195eSAndrew Gallatin case SIOCSIFCAP: 3133a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3134b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 3135b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 3136b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 3137aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 3138aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 3139aed8e389SAndrew Gallatin | CSUM_TSO); 3140b2fc195eSAndrew Gallatin } else { 3141b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 3142b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 3143b2fc195eSAndrew Gallatin } 3144b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 3145b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 3146b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 31475e7d8541SAndrew Gallatin sc->csum_flag = 0; 3148b2fc195eSAndrew Gallatin } else { 3149b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 31505e7d8541SAndrew Gallatin sc->csum_flag = 1; 3151b2fc195eSAndrew Gallatin } 3152b2fc195eSAndrew Gallatin } 3153aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 3154aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 3155aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 3156aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 3157aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 3158aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 3159aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 3160aed8e389SAndrew Gallatin } else { 3161aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 3162aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 3163aed8e389SAndrew Gallatin err = EINVAL; 3164aed8e389SAndrew Gallatin } 3165aed8e389SAndrew Gallatin } 3166f04b33f8SAndrew Gallatin if (mask & IFCAP_LRO) { 3167f04b33f8SAndrew Gallatin if (IFCAP_LRO & ifp->if_capenable) 3168f04b33f8SAndrew Gallatin err = mxge_change_lro_locked(sc, 0); 3169f04b33f8SAndrew Gallatin else 3170f04b33f8SAndrew Gallatin err = mxge_change_lro_locked(sc, mxge_lro_cnt); 3171f04b33f8SAndrew Gallatin } 3172c792928fSAndrew Gallatin if (mask & IFCAP_VLAN_HWTAGGING) 3173c792928fSAndrew Gallatin ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 3174a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3175c792928fSAndrew Gallatin VLAN_CAPABILITIES(ifp); 3176c792928fSAndrew Gallatin 3177b2fc195eSAndrew Gallatin break; 3178b2fc195eSAndrew Gallatin 3179b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 3180b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 3181b2fc195eSAndrew Gallatin &sc->media, command); 3182b2fc195eSAndrew Gallatin break; 3183b2fc195eSAndrew Gallatin 3184b2fc195eSAndrew Gallatin default: 3185b2fc195eSAndrew Gallatin err = ENOTTY; 3186b2fc195eSAndrew Gallatin } 3187b2fc195eSAndrew Gallatin return err; 3188b2fc195eSAndrew Gallatin } 3189b2fc195eSAndrew Gallatin 3190b2fc195eSAndrew Gallatin static void 31916d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 3192b2fc195eSAndrew Gallatin { 3193b2fc195eSAndrew Gallatin 31946d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 31956d87a65dSAndrew Gallatin &mxge_flow_control); 31966d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 31976d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 31986d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 31996d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 3200d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 3201d91b1b49SAndrew Gallatin &mxge_force_firmware); 32025e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 32035e7d8541SAndrew Gallatin &mxge_deassert_wait); 32045e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 32055e7d8541SAndrew Gallatin &mxge_verbose); 3206dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 3207053e637fSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.lro_cnt", &sc->lro_cnt); 3208f04b33f8SAndrew Gallatin printf("%d %d\n", sc->lro_cnt, mxge_lro_cnt); 3209f04b33f8SAndrew Gallatin if (sc->lro_cnt != 0) 3210f04b33f8SAndrew Gallatin mxge_lro_cnt = sc->lro_cnt; 3211b2fc195eSAndrew Gallatin 32125e7d8541SAndrew Gallatin if (bootverbose) 32135e7d8541SAndrew Gallatin mxge_verbose = 1; 32146d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 32156d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 3216dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 3217dce01b9bSAndrew Gallatin mxge_ticks = hz; 32186d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 3219053e637fSAndrew Gallatin 3220b2fc195eSAndrew Gallatin } 3221b2fc195eSAndrew Gallatin 3222b2fc195eSAndrew Gallatin static int 32236d87a65dSAndrew Gallatin mxge_attach(device_t dev) 3224b2fc195eSAndrew Gallatin { 32256d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3226b2fc195eSAndrew Gallatin struct ifnet *ifp; 3227dce01b9bSAndrew Gallatin int count, rid, err; 3228b2fc195eSAndrew Gallatin 3229b2fc195eSAndrew Gallatin sc->dev = dev; 32306d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 3231b2fc195eSAndrew Gallatin 3232b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 3233b2fc195eSAndrew Gallatin 1, /* alignment */ 3234b2fc195eSAndrew Gallatin 4096, /* boundary */ 3235b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 3236b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 3237b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 3238aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 32395e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 3240b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 3241b2fc195eSAndrew Gallatin 0, /* flags */ 3242b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 3243b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 3244b2fc195eSAndrew Gallatin 3245b2fc195eSAndrew Gallatin if (err != 0) { 3246b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 3247b2fc195eSAndrew Gallatin err); 3248b2fc195eSAndrew Gallatin goto abort_with_nothing; 3249b2fc195eSAndrew Gallatin } 3250b2fc195eSAndrew Gallatin 3251b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 3252b2fc195eSAndrew Gallatin if (ifp == NULL) { 3253b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 3254b2fc195eSAndrew Gallatin err = ENOSPC; 3255b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 3256b2fc195eSAndrew Gallatin } 3257a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 3258a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3259a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 3260a98d6cd7SAndrew Gallatin snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", 3261a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 3262a98d6cd7SAndrew Gallatin mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); 3263a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 3264a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 3265a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 3266b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 3267b2fc195eSAndrew Gallatin 3268dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 3269d91b1b49SAndrew Gallatin 3270dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 3271b2fc195eSAndrew Gallatin 3272b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 3273b2fc195eSAndrew Gallatin rid = PCIR_BARS; 3274b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 3275b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 3276b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 3277b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 3278b2fc195eSAndrew Gallatin err = ENXIO; 3279b2fc195eSAndrew Gallatin goto abort_with_lock; 3280b2fc195eSAndrew Gallatin } 3281b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 3282b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 3283b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 3284b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 3285b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 3286b2fc195eSAndrew Gallatin err = ENXIO; 3287b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3288b2fc195eSAndrew Gallatin } 3289b2fc195eSAndrew Gallatin 3290b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 3291b2fc195eSAndrew Gallatin lanai SRAM */ 32926d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 3293b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 3294b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 32956d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 3296b2fc195eSAndrew Gallatin sc->eeprom_strings, 32976d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 32986d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 3299b2fc195eSAndrew Gallatin if (err != 0) 3300b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3301b2fc195eSAndrew Gallatin 3302b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 33036d87a65dSAndrew Gallatin mxge_enable_wc(sc); 3304b2fc195eSAndrew Gallatin 3305b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 33066d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 33076d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 3308b2fc195eSAndrew Gallatin if (err != 0) 3309b2fc195eSAndrew Gallatin goto abort_with_mem_res; 3310b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 33116d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 3312b2fc195eSAndrew Gallatin if (err != 0) 3313b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 3314b2fc195eSAndrew Gallatin 33156d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 3316b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 3317b2fc195eSAndrew Gallatin if (err != 0) 3318b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 33195e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 3320b2fc195eSAndrew Gallatin 3321a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 3322a98d6cd7SAndrew Gallatin if (err != 0) 3323a98d6cd7SAndrew Gallatin goto abort_with_fw_stats; 3324b2fc195eSAndrew Gallatin 3325b2fc195eSAndrew Gallatin /* Add our ithread */ 3326dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 3327dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 3328dc8731d4SAndrew Gallatin rid = 1; 3329dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 3330dc8731d4SAndrew Gallatin } else { 3331b2fc195eSAndrew Gallatin rid = 0; 3332dc8731d4SAndrew Gallatin } 3333b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 3334b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 3335b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 3336b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 3337adae7080SAndrew Gallatin goto abort_with_dmabench; 3338b2fc195eSAndrew Gallatin } 3339d91b1b49SAndrew Gallatin if (mxge_verbose) 3340dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 3341dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 3342dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 33438fe615baSAndrew Gallatin /* select & load the firmware */ 33448fe615baSAndrew Gallatin err = mxge_select_firmware(sc); 3345b2fc195eSAndrew Gallatin if (err != 0) 3346b2fc195eSAndrew Gallatin goto abort_with_irq_res; 33475e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 3348adae7080SAndrew Gallatin err = mxge_reset(sc, 0); 3349b2fc195eSAndrew Gallatin if (err != 0) 3350b2fc195eSAndrew Gallatin goto abort_with_irq_res; 3351b2fc195eSAndrew Gallatin 3352a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 3353a98d6cd7SAndrew Gallatin if (err != 0) { 3354a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 3355a98d6cd7SAndrew Gallatin goto abort_with_irq_res; 3356a98d6cd7SAndrew Gallatin } 3357a98d6cd7SAndrew Gallatin 3358a98d6cd7SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 3359a98d6cd7SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 3360ef544f63SPaolo Pisati NULL, mxge_intr, sc, &sc->ih); 3361a98d6cd7SAndrew Gallatin if (err != 0) { 3362a98d6cd7SAndrew Gallatin goto abort_with_rings; 3363a98d6cd7SAndrew Gallatin } 3364b2fc195eSAndrew Gallatin /* hook into the network stack */ 3365b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 3366b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 3367c792928fSAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | 3368f04b33f8SAndrew Gallatin IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | 3369f04b33f8SAndrew Gallatin IFCAP_VLAN_HWCSUM | IFCAP_LRO; 3370c792928fSAndrew Gallatin 3371053e637fSAndrew Gallatin sc->max_mtu = mxge_max_mtu(sc); 3372053e637fSAndrew Gallatin if (sc->max_mtu >= 9000) 3373053e637fSAndrew Gallatin ifp->if_capabilities |= IFCAP_JUMBO_MTU; 3374053e637fSAndrew Gallatin else 3375053e637fSAndrew Gallatin device_printf(dev, "MTU limited to %d. Install " 3376adae7080SAndrew Gallatin "latest firmware for 9000 byte jumbo support\n", 3377053e637fSAndrew Gallatin sc->max_mtu - ETHER_HDR_LEN); 3378aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 3379b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 3380f04b33f8SAndrew Gallatin if (sc->lro_cnt == 0) 3381f04b33f8SAndrew Gallatin ifp->if_capenable &= ~IFCAP_LRO; 33825e7d8541SAndrew Gallatin sc->csum_flag = 1; 33836d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 3384b2fc195eSAndrew Gallatin ifp->if_softc = sc; 3385b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 33866d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 33876d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 3388b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 3389b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 3390053e637fSAndrew Gallatin if (ifp->if_capabilities & IFCAP_JUMBO_MTU) 3391c792928fSAndrew Gallatin ifp->if_mtu = 9000; 3392b2fc195eSAndrew Gallatin 3393b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 33946d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 33956d87a65dSAndrew Gallatin mxge_media_status); 3396b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 33976d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 3398b2fc195eSAndrew Gallatin return 0; 3399b2fc195eSAndrew Gallatin 3400a98d6cd7SAndrew Gallatin abort_with_rings: 3401a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3402b2fc195eSAndrew Gallatin abort_with_irq_res: 3403dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3404dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3405dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3406dc8731d4SAndrew Gallatin pci_release_msi(dev); 3407a98d6cd7SAndrew Gallatin abort_with_dmabench: 3408a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 34095e7d8541SAndrew Gallatin abort_with_fw_stats: 34106d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3411b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 34126d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 3413b2fc195eSAndrew Gallatin abort_with_cmd_dma: 34146d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3415b2fc195eSAndrew Gallatin abort_with_mem_res: 3416b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3417b2fc195eSAndrew Gallatin abort_with_lock: 3418b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3419a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3420a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3421a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3422b2fc195eSAndrew Gallatin if_free(ifp); 3423b2fc195eSAndrew Gallatin abort_with_parent_dmat: 3424b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3425b2fc195eSAndrew Gallatin 3426b2fc195eSAndrew Gallatin abort_with_nothing: 3427b2fc195eSAndrew Gallatin return err; 3428b2fc195eSAndrew Gallatin } 3429b2fc195eSAndrew Gallatin 3430b2fc195eSAndrew Gallatin static int 34316d87a65dSAndrew Gallatin mxge_detach(device_t dev) 3432b2fc195eSAndrew Gallatin { 34336d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3434b2fc195eSAndrew Gallatin 3435c792928fSAndrew Gallatin if (sc->ifp->if_vlantrunk != NULL) { 3436c792928fSAndrew Gallatin device_printf(sc->dev, 3437c792928fSAndrew Gallatin "Detach vlans before removing module\n"); 3438c792928fSAndrew Gallatin return EBUSY; 3439c792928fSAndrew Gallatin } 3440a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3441b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 34426d87a65dSAndrew Gallatin mxge_close(sc); 3443dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3444a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3445b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 3446dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 3447091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 3448a98d6cd7SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 3449a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3450dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3451dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3452dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3453dc8731d4SAndrew Gallatin pci_release_msi(dev); 3454dc8731d4SAndrew Gallatin 34555e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 34566d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3457a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 34586d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 34596d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3460b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3461b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3462a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3463a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3464a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3465b2fc195eSAndrew Gallatin if_free(sc->ifp); 3466b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3467b2fc195eSAndrew Gallatin return 0; 3468b2fc195eSAndrew Gallatin } 3469b2fc195eSAndrew Gallatin 3470b2fc195eSAndrew Gallatin static int 34716d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 3472b2fc195eSAndrew Gallatin { 3473b2fc195eSAndrew Gallatin return 0; 3474b2fc195eSAndrew Gallatin } 3475b2fc195eSAndrew Gallatin 3476b2fc195eSAndrew Gallatin /* 3477b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 3478b2fc195eSAndrew Gallatin 3479b2fc195eSAndrew Gallatin Local Variables: 3480b2fc195eSAndrew Gallatin c-file-style:"linux" 3481b2fc195eSAndrew Gallatin tab-width:8 3482b2fc195eSAndrew Gallatin End: 3483b2fc195eSAndrew Gallatin */ 3484