16d87a65dSAndrew Gallatin /****************************************************************************** 2b2fc195eSAndrew Gallatin 3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc. 4b2fc195eSAndrew Gallatin All rights reserved. 5b2fc195eSAndrew Gallatin 6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without 7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met: 8b2fc195eSAndrew Gallatin 9b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice, 10b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer. 11b2fc195eSAndrew Gallatin 12b2fc195eSAndrew Gallatin 2. Redistributions in binary form must reproduce the above copyright 13b2fc195eSAndrew Gallatin notice, this list of conditions and the following disclaimer in the 14b2fc195eSAndrew Gallatin documentation and/or other materials provided with the distribution. 15b2fc195eSAndrew Gallatin 16b2fc195eSAndrew Gallatin 3. Neither the name of the Myricom Inc, nor the names of its 17b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 18b2fc195eSAndrew Gallatin this software without specific prior written permission. 19b2fc195eSAndrew Gallatin 20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 31b2fc195eSAndrew Gallatin 32b2fc195eSAndrew Gallatin ***************************************************************************/ 33b2fc195eSAndrew Gallatin 34b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 36b2fc195eSAndrew Gallatin 37b2fc195eSAndrew Gallatin #include <sys/param.h> 38b2fc195eSAndrew Gallatin #include <sys/systm.h> 39b2fc195eSAndrew Gallatin #include <sys/linker.h> 40b2fc195eSAndrew Gallatin #include <sys/firmware.h> 41b2fc195eSAndrew Gallatin #include <sys/endian.h> 42b2fc195eSAndrew Gallatin #include <sys/sockio.h> 43b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 44b2fc195eSAndrew Gallatin #include <sys/malloc.h> 45b2fc195eSAndrew Gallatin #include <sys/kdb.h> 46b2fc195eSAndrew Gallatin #include <sys/kernel.h> 47b2fc195eSAndrew Gallatin #include <sys/module.h> 48b2fc195eSAndrew Gallatin #include <sys/memrange.h> 49b2fc195eSAndrew Gallatin #include <sys/socket.h> 50b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 51b2fc195eSAndrew Gallatin #include <sys/sx.h> 52b2fc195eSAndrew Gallatin 53b2fc195eSAndrew Gallatin #include <net/if.h> 54b2fc195eSAndrew Gallatin #include <net/if_arp.h> 55b2fc195eSAndrew Gallatin #include <net/ethernet.h> 56b2fc195eSAndrew Gallatin #include <net/if_dl.h> 57b2fc195eSAndrew Gallatin #include <net/if_media.h> 58b2fc195eSAndrew Gallatin 59b2fc195eSAndrew Gallatin #include <net/bpf.h> 60b2fc195eSAndrew Gallatin 61b2fc195eSAndrew Gallatin #include <net/if_types.h> 62b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h> 63b2fc195eSAndrew Gallatin #include <net/zlib.h> 64b2fc195eSAndrew Gallatin 65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 66b2fc195eSAndrew Gallatin #include <netinet/in.h> 67b2fc195eSAndrew Gallatin #include <netinet/ip.h> 68aed8e389SAndrew Gallatin #include <netinet/tcp.h> 69b2fc195eSAndrew Gallatin 70b2fc195eSAndrew Gallatin #include <machine/bus.h> 71b2fc195eSAndrew Gallatin #include <machine/resource.h> 72b2fc195eSAndrew Gallatin #include <sys/bus.h> 73b2fc195eSAndrew Gallatin #include <sys/rman.h> 74b2fc195eSAndrew Gallatin 75b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 76b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 77b2fc195eSAndrew Gallatin 78b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 79b2fc195eSAndrew Gallatin #include <vm/pmap.h> 80b2fc195eSAndrew Gallatin 816d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 826d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 836d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 84b2fc195eSAndrew Gallatin 85b2fc195eSAndrew Gallatin /* tunable params */ 866d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 87d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0; 885e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024; 896d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 905e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 916d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 925e7d8541SAndrew Gallatin static int mxge_verbose = 0; 93dce01b9bSAndrew Gallatin static int mxge_ticks; 946d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 956d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 96b2fc195eSAndrew Gallatin 976d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 986d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 996d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 1006d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1016d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 102b2fc195eSAndrew Gallatin 1036d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 104b2fc195eSAndrew Gallatin { 105b2fc195eSAndrew Gallatin /* Device interface */ 1066d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1076d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1086d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1096d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 110b2fc195eSAndrew Gallatin {0, 0} 111b2fc195eSAndrew Gallatin }; 112b2fc195eSAndrew Gallatin 1136d87a65dSAndrew Gallatin static driver_t mxge_driver = 114b2fc195eSAndrew Gallatin { 1156d87a65dSAndrew Gallatin "mxge", 1166d87a65dSAndrew Gallatin mxge_methods, 1176d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 118b2fc195eSAndrew Gallatin }; 119b2fc195eSAndrew Gallatin 1206d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 121b2fc195eSAndrew Gallatin 122b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1236d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1246d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 125b2fc195eSAndrew Gallatin 126b2fc195eSAndrew Gallatin static int 1276d87a65dSAndrew Gallatin mxge_probe(device_t dev) 128b2fc195eSAndrew Gallatin { 1296d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1306d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 131b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 132b2fc195eSAndrew Gallatin return 0; 133b2fc195eSAndrew Gallatin } 134b2fc195eSAndrew Gallatin return ENXIO; 135b2fc195eSAndrew Gallatin } 136b2fc195eSAndrew Gallatin 137b2fc195eSAndrew Gallatin static void 1386d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 139b2fc195eSAndrew Gallatin { 140b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 141b2fc195eSAndrew Gallatin vm_paddr_t pa; 142b2fc195eSAndrew Gallatin vm_offset_t len; 143b2fc195eSAndrew Gallatin int err, action; 144b2fc195eSAndrew Gallatin 145b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 146b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 147b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 148b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 149b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 150b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1516d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 152b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 153b2fc195eSAndrew Gallatin if (err != 0) { 154b2fc195eSAndrew Gallatin device_printf(sc->dev, 155b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 156b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 157b2fc195eSAndrew Gallatin } else { 158b2fc195eSAndrew Gallatin sc->wc = 1; 159b2fc195eSAndrew Gallatin } 160b2fc195eSAndrew Gallatin } 161b2fc195eSAndrew Gallatin 162b2fc195eSAndrew Gallatin 163b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 164b2fc195eSAndrew Gallatin static void 1656d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 166b2fc195eSAndrew Gallatin int error) 167b2fc195eSAndrew Gallatin { 168b2fc195eSAndrew Gallatin if (error == 0) { 169b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 170b2fc195eSAndrew Gallatin } 171b2fc195eSAndrew Gallatin } 172b2fc195eSAndrew Gallatin 173b2fc195eSAndrew Gallatin static int 1746d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 175b2fc195eSAndrew Gallatin bus_size_t alignment) 176b2fc195eSAndrew Gallatin { 177b2fc195eSAndrew Gallatin int err; 178b2fc195eSAndrew Gallatin device_t dev = sc->dev; 179b2fc195eSAndrew Gallatin 180b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 181b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 182b2fc195eSAndrew Gallatin alignment, /* alignment */ 183b2fc195eSAndrew Gallatin 4096, /* boundary */ 184b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 185b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 186b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 187b2fc195eSAndrew Gallatin bytes, /* maxsize */ 188b2fc195eSAndrew Gallatin 1, /* num segs */ 189b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 190b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 191b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 192b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 193b2fc195eSAndrew Gallatin if (err != 0) { 194b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 195b2fc195eSAndrew Gallatin return err; 196b2fc195eSAndrew Gallatin } 197b2fc195eSAndrew Gallatin 198b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 199b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 200b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 201b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 202b2fc195eSAndrew Gallatin if (err != 0) { 203b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 204b2fc195eSAndrew Gallatin goto abort_with_dmat; 205b2fc195eSAndrew Gallatin } 206b2fc195eSAndrew Gallatin 207b2fc195eSAndrew Gallatin /* load the memory */ 208b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2096d87a65dSAndrew Gallatin mxge_dmamap_callback, 210b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 211b2fc195eSAndrew Gallatin if (err != 0) { 212b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 213b2fc195eSAndrew Gallatin goto abort_with_mem; 214b2fc195eSAndrew Gallatin } 215b2fc195eSAndrew Gallatin return 0; 216b2fc195eSAndrew Gallatin 217b2fc195eSAndrew Gallatin abort_with_mem: 218b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 219b2fc195eSAndrew Gallatin abort_with_dmat: 220b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 221b2fc195eSAndrew Gallatin return err; 222b2fc195eSAndrew Gallatin } 223b2fc195eSAndrew Gallatin 224b2fc195eSAndrew Gallatin 225b2fc195eSAndrew Gallatin static void 2266d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 227b2fc195eSAndrew Gallatin { 228b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 229b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 230b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 231b2fc195eSAndrew Gallatin } 232b2fc195eSAndrew Gallatin 233b2fc195eSAndrew Gallatin /* 234b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 235b2fc195eSAndrew Gallatin * SN=x\0 236b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 237b2fc195eSAndrew Gallatin * PC=text\0 238b2fc195eSAndrew Gallatin */ 239b2fc195eSAndrew Gallatin 240b2fc195eSAndrew Gallatin static int 2416d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 242b2fc195eSAndrew Gallatin { 2436d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 244b2fc195eSAndrew Gallatin 245b2fc195eSAndrew Gallatin char *ptr, *limit; 246b2fc195eSAndrew Gallatin int i, found_mac; 247b2fc195eSAndrew Gallatin 248b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2496d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 250b2fc195eSAndrew Gallatin found_mac = 0; 251b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 252b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2535e7d8541SAndrew Gallatin ptr += 1; 254b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 255b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2565e7d8541SAndrew Gallatin ptr += 3; 257b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 258b2fc195eSAndrew Gallatin goto abort; 259b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 260b2fc195eSAndrew Gallatin found_mac = 1; 261b2fc195eSAndrew Gallatin } 2625e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2635e7d8541SAndrew Gallatin ptr += 3; 2645e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2655e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2665e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2675e7d8541SAndrew Gallatin ptr += 3; 2685e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2695e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 270b2fc195eSAndrew Gallatin } 2716d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 272b2fc195eSAndrew Gallatin } 273b2fc195eSAndrew Gallatin 274b2fc195eSAndrew Gallatin if (found_mac) 275b2fc195eSAndrew Gallatin return 0; 276b2fc195eSAndrew Gallatin 277b2fc195eSAndrew Gallatin abort: 278b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 279b2fc195eSAndrew Gallatin 280b2fc195eSAndrew Gallatin return ENXIO; 281b2fc195eSAndrew Gallatin } 282b2fc195eSAndrew Gallatin 283b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 284b2fc195eSAndrew Gallatin static int 2856d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 286b2fc195eSAndrew Gallatin { 287b2fc195eSAndrew Gallatin uint32_t val; 288b2fc195eSAndrew Gallatin unsigned long off; 289b2fc195eSAndrew Gallatin char *va, *cfgptr; 290b2fc195eSAndrew Gallatin uint16_t vendor_id, device_id; 291b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 292b2fc195eSAndrew Gallatin uint32_t *ptr32; 293b2fc195eSAndrew Gallatin 294b2fc195eSAndrew Gallatin /* XXXX 295b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 296b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 297b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 298b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 299b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 300b2fc195eSAndrew Gallatin */ 301b2fc195eSAndrew Gallatin #if 0 302b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 303b2fc195eSAndrew Gallatin config space */ 304b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 305b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 306b2fc195eSAndrew Gallatin val |= 0x40; 307b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 308b2fc195eSAndrew Gallatin return 0; 309b2fc195eSAndrew Gallatin } 310b2fc195eSAndrew Gallatin #endif 311b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 312b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 313b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 314b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 315b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 316b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 317b2fc195eSAndrew Gallatin */ 318b2fc195eSAndrew Gallatin 319b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 320b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 321b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 322b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 323b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 324b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 325b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 326b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 327b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 328b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 329b2fc195eSAndrew Gallatin 330b2fc195eSAndrew Gallatin off = 0xe0000000UL 331b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 332b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 333b2fc195eSAndrew Gallatin + 8 * slot); 334b2fc195eSAndrew Gallatin 335b2fc195eSAndrew Gallatin /* map it into the kernel */ 336b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 337b2fc195eSAndrew Gallatin 338b2fc195eSAndrew Gallatin 339b2fc195eSAndrew Gallatin if (va == NULL) { 340b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 341b2fc195eSAndrew Gallatin return EIO; 342b2fc195eSAndrew Gallatin } 343b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 344b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 345b2fc195eSAndrew Gallatin 346b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 347b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 348b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 349b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 350b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 351b2fc195eSAndrew Gallatin vendor_id, device_id); 352b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 353b2fc195eSAndrew Gallatin return EIO; 354b2fc195eSAndrew Gallatin } 355b2fc195eSAndrew Gallatin 356b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 357b2fc195eSAndrew Gallatin val = *ptr32; 358b2fc195eSAndrew Gallatin 359b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 360b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 361b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 362b2fc195eSAndrew Gallatin return EIO; 363b2fc195eSAndrew Gallatin } 364b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 365b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3665e7d8541SAndrew Gallatin if (mxge_verbose) 367b2fc195eSAndrew Gallatin device_printf(sc->dev, 3685e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 3695e7d8541SAndrew Gallatin "at %d:%d:%d\n", 370b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 371b2fc195eSAndrew Gallatin return 0; 372b2fc195eSAndrew Gallatin } 373b2fc195eSAndrew Gallatin #else 374b2fc195eSAndrew Gallatin static int 3756d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 376b2fc195eSAndrew Gallatin { 377b2fc195eSAndrew Gallatin device_printf(sc->dev, 378b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 379b2fc195eSAndrew Gallatin return ENXIO; 380b2fc195eSAndrew Gallatin } 381b2fc195eSAndrew Gallatin #endif 382b2fc195eSAndrew Gallatin /* 383b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 384b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 385b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 386b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 387b2fc195eSAndrew Gallatin * ECRC generation (if supported). 388b2fc195eSAndrew Gallatin * 389b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 390b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 391b2fc195eSAndrew Gallatin * 392b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 393b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 394b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 395b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 396b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 397b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 398b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 399b2fc195eSAndrew Gallatin */ 400b2fc195eSAndrew Gallatin 401b2fc195eSAndrew Gallatin static void 4026d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 403b2fc195eSAndrew Gallatin { 404b2fc195eSAndrew Gallatin int err, aligned = 0; 405b2fc195eSAndrew Gallatin device_t pdev; 406b2fc195eSAndrew Gallatin uint16_t pvend, pdid; 407b2fc195eSAndrew Gallatin 408d91b1b49SAndrew Gallatin 409d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 410d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 411d91b1b49SAndrew Gallatin aligned = 1; 412d91b1b49SAndrew Gallatin else 413d91b1b49SAndrew Gallatin aligned = 0; 414d91b1b49SAndrew Gallatin if (mxge_verbose) 415d91b1b49SAndrew Gallatin device_printf(sc->dev, 416d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 417d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 418d91b1b49SAndrew Gallatin goto abort; 419d91b1b49SAndrew Gallatin } 420d91b1b49SAndrew Gallatin 421d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 422d91b1b49SAndrew Gallatin firmware and skip any checks */ 423d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 424d91b1b49SAndrew Gallatin device_printf(sc->dev, 425d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 426d91b1b49SAndrew Gallatin sc->link_width); 427d91b1b49SAndrew Gallatin aligned = 1; 428d91b1b49SAndrew Gallatin goto abort; 429d91b1b49SAndrew Gallatin } 430d91b1b49SAndrew Gallatin 431b2fc195eSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 432b2fc195eSAndrew Gallatin if (pdev == NULL) { 433b2fc195eSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 434b2fc195eSAndrew Gallatin goto abort; 435b2fc195eSAndrew Gallatin } 436b2fc195eSAndrew Gallatin pvend = pci_read_config(pdev, PCIR_VENDOR, 2); 437b2fc195eSAndrew Gallatin pdid = pci_read_config(pdev, PCIR_DEVICE, 2); 438b2fc195eSAndrew Gallatin 439b2fc195eSAndrew Gallatin /* see if we can enable ECRC's on an upstream 440b2fc195eSAndrew Gallatin Nvidia bridge */ 4416d87a65dSAndrew Gallatin if (mxge_nvidia_ecrc_enable && 442b2fc195eSAndrew Gallatin (pvend == 0x10de && pdid == 0x005d)) { 4436d87a65dSAndrew Gallatin err = mxge_enable_nvidia_ecrc(sc, pdev); 444b2fc195eSAndrew Gallatin if (err == 0) { 445b2fc195eSAndrew Gallatin aligned = 1; 4465e7d8541SAndrew Gallatin if (mxge_verbose) 447b2fc195eSAndrew Gallatin device_printf(sc->dev, 4485e7d8541SAndrew Gallatin "Assuming aligned completions" 4495e7d8541SAndrew Gallatin " (ECRC)\n"); 450b2fc195eSAndrew Gallatin } 451b2fc195eSAndrew Gallatin } 452b2fc195eSAndrew Gallatin /* see if the upstream bridge is known to 453b2fc195eSAndrew Gallatin provided aligned completions */ 454b2fc195eSAndrew Gallatin if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) || 4550fa7f681SAndrew Gallatin /* PLX */ (pvend == 0x10b5 && pdid == 0x8532) || 4560fa7f681SAndrew Gallatin /* Intel */ (pvend == 0x8086 && 457d91b1b49SAndrew Gallatin /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) || 458d91b1b49SAndrew Gallatin /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) { 459d91b1b49SAndrew Gallatin aligned = 1; 4605e7d8541SAndrew Gallatin if (mxge_verbose) 461b2fc195eSAndrew Gallatin device_printf(sc->dev, 4625e7d8541SAndrew Gallatin "Assuming aligned completions " 4635e7d8541SAndrew Gallatin "(0x%x:0x%x)\n", pvend, pdid); 464b2fc195eSAndrew Gallatin } 465b2fc195eSAndrew Gallatin 466b2fc195eSAndrew Gallatin abort: 467b2fc195eSAndrew Gallatin if (aligned) { 4686d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 469b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 470b2fc195eSAndrew Gallatin } else { 4716d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 472b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 473b2fc195eSAndrew Gallatin } 474b2fc195eSAndrew Gallatin } 475b2fc195eSAndrew Gallatin 476b2fc195eSAndrew Gallatin union qualhack 477b2fc195eSAndrew Gallatin { 478b2fc195eSAndrew Gallatin const char *ro_char; 479b2fc195eSAndrew Gallatin char *rw_char; 480b2fc195eSAndrew Gallatin }; 481b2fc195eSAndrew Gallatin 4824da0d523SAndrew Gallatin static int 4834da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 4844da0d523SAndrew Gallatin { 485b824b7d8SAndrew Gallatin 4864da0d523SAndrew Gallatin 4874da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 4884da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 4894da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 4904da0d523SAndrew Gallatin return EIO; 4914da0d523SAndrew Gallatin } 4924da0d523SAndrew Gallatin 4934da0d523SAndrew Gallatin /* save firmware version for sysctl */ 4944da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 4954da0d523SAndrew Gallatin if (mxge_verbose) 4964da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 4974da0d523SAndrew Gallatin 498b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 499b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 5004da0d523SAndrew Gallatin 501b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 502b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 5034da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 5044da0d523SAndrew Gallatin sc->fw_version); 5054da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 5064da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 5074da0d523SAndrew Gallatin return EINVAL; 5084da0d523SAndrew Gallatin } 5094da0d523SAndrew Gallatin return 0; 5104da0d523SAndrew Gallatin 5114da0d523SAndrew Gallatin } 512b2fc195eSAndrew Gallatin 513b2fc195eSAndrew Gallatin static int 5146d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 515b2fc195eSAndrew Gallatin { 51633d54970SLuigi Rizzo const struct firmware *fw; 517b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 518b2fc195eSAndrew Gallatin unsigned hdr_offset; 519b2fc195eSAndrew Gallatin const char *fw_data; 520b2fc195eSAndrew Gallatin union qualhack hack; 521b2fc195eSAndrew Gallatin int status; 5224da0d523SAndrew Gallatin unsigned int i; 5234da0d523SAndrew Gallatin char dummy; 524b2fc195eSAndrew Gallatin 525b2fc195eSAndrew Gallatin 526b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 527b2fc195eSAndrew Gallatin 528b2fc195eSAndrew Gallatin if (fw == NULL) { 529b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 530b2fc195eSAndrew Gallatin sc->fw_name); 531b2fc195eSAndrew Gallatin return ENOENT; 532b2fc195eSAndrew Gallatin } 533b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 534b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 535b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 536b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 537b2fc195eSAndrew Gallatin status = ENOSPC; 538b2fc195eSAndrew Gallatin goto abort_with_fw; 539b2fc195eSAndrew Gallatin } 540b2fc195eSAndrew Gallatin *limit = fw->datasize; 541b2fc195eSAndrew Gallatin 542b2fc195eSAndrew Gallatin /* check id */ 543b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 544b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 545b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 546b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 547b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 548b2fc195eSAndrew Gallatin status = EIO; 549b2fc195eSAndrew Gallatin goto abort_with_fw; 550b2fc195eSAndrew Gallatin } 551b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 552b2fc195eSAndrew Gallatin 5534da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 5544da0d523SAndrew Gallatin if (status != 0) 5554da0d523SAndrew Gallatin goto abort_with_fw; 556b2fc195eSAndrew Gallatin 557b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 558b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 5594da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 5604da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 5614da0d523SAndrew Gallatin hack.rw_char + i, 5624da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 5634da0d523SAndrew Gallatin mb(); 5644da0d523SAndrew Gallatin dummy = *sc->sram; 5654da0d523SAndrew Gallatin mb(); 5664da0d523SAndrew Gallatin } 567b2fc195eSAndrew Gallatin 568b2fc195eSAndrew Gallatin status = 0; 569b2fc195eSAndrew Gallatin abort_with_fw: 570b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 571b2fc195eSAndrew Gallatin return status; 572b2fc195eSAndrew Gallatin } 573b2fc195eSAndrew Gallatin 574b2fc195eSAndrew Gallatin /* 575b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 576b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 577b2fc195eSAndrew Gallatin */ 578b2fc195eSAndrew Gallatin 579b2fc195eSAndrew Gallatin static void 5806d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 581b2fc195eSAndrew Gallatin { 582b2fc195eSAndrew Gallatin char buf_bytes[72]; 583b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 584b2fc195eSAndrew Gallatin volatile char *submit; 585b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 586b2fc195eSAndrew Gallatin int i; 587b2fc195eSAndrew Gallatin 588b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 589b2fc195eSAndrew Gallatin 590b2fc195eSAndrew Gallatin /* clear confirmation addr */ 591b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 592b2fc195eSAndrew Gallatin *confirm = 0; 593b2fc195eSAndrew Gallatin mb(); 594b2fc195eSAndrew Gallatin 595b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 596b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 597b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 598b2fc195eSAndrew Gallatin */ 599b2fc195eSAndrew Gallatin 6006d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6016d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 602b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 603b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 604b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 6056d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 6066d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 607b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 608b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 609b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 610b2fc195eSAndrew Gallatin 611b2fc195eSAndrew Gallatin 6120fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 613b2fc195eSAndrew Gallatin 6146d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 615b2fc195eSAndrew Gallatin mb(); 616b2fc195eSAndrew Gallatin DELAY(1000); 617b2fc195eSAndrew Gallatin mb(); 618b2fc195eSAndrew Gallatin i = 0; 619b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 620b2fc195eSAndrew Gallatin DELAY(1000); 621b2fc195eSAndrew Gallatin i++; 622b2fc195eSAndrew Gallatin } 623b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 624b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 625b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 626b2fc195eSAndrew Gallatin *confirm); 627b2fc195eSAndrew Gallatin } 628b2fc195eSAndrew Gallatin return; 629b2fc195eSAndrew Gallatin } 630b2fc195eSAndrew Gallatin 631b2fc195eSAndrew Gallatin static int 6326d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 633b2fc195eSAndrew Gallatin { 634b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 635b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 636b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 6370fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 638b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 639b2fc195eSAndrew Gallatin int sleep_total = 0; 640b2fc195eSAndrew Gallatin 641b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 642b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 643b2fc195eSAndrew Gallatin 644b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 645b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 646b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 647b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 6486d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6496d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 650b2fc195eSAndrew Gallatin 651b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 652b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 653a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 654b2fc195eSAndrew Gallatin response->result = 0xffffffff; 655b2fc195eSAndrew Gallatin mb(); 6566d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 657b2fc195eSAndrew Gallatin 6585e7d8541SAndrew Gallatin /* wait up to 20ms */ 6595e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 660b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 661b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 662b2fc195eSAndrew Gallatin mb(); 663b2fc195eSAndrew Gallatin if (response->result != 0xffffffff) { 664b2fc195eSAndrew Gallatin if (response->result == 0) { 665b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 666a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 667b2fc195eSAndrew Gallatin return 0; 668b2fc195eSAndrew Gallatin } else { 669b2fc195eSAndrew Gallatin device_printf(sc->dev, 6706d87a65dSAndrew Gallatin "mxge: command %d " 671b2fc195eSAndrew Gallatin "failed, result = %d\n", 672b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 673a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 674b2fc195eSAndrew Gallatin return ENXIO; 675b2fc195eSAndrew Gallatin } 676b2fc195eSAndrew Gallatin } 6775e7d8541SAndrew Gallatin DELAY(1000); 678b2fc195eSAndrew Gallatin } 679a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 6806d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 681b2fc195eSAndrew Gallatin "result = %d\n", 682b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 683b2fc195eSAndrew Gallatin return EAGAIN; 684b2fc195eSAndrew Gallatin } 685b2fc195eSAndrew Gallatin 6864da0d523SAndrew Gallatin static int 6874da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 6884da0d523SAndrew Gallatin { 6894da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 6904da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 6914da0d523SAndrew Gallatin size_t hdr_offset; 6924da0d523SAndrew Gallatin int status; 6934da0d523SAndrew Gallatin 6944da0d523SAndrew Gallatin /* find running firmware header */ 6954da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 6964da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 6974da0d523SAndrew Gallatin 6984da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 6994da0d523SAndrew Gallatin device_printf(sc->dev, 7004da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 7014da0d523SAndrew Gallatin (int)hdr_offset); 7024da0d523SAndrew Gallatin return EIO; 7034da0d523SAndrew Gallatin } 7044da0d523SAndrew Gallatin 7054da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 7064da0d523SAndrew Gallatin * validate firmware */ 7074da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 7084da0d523SAndrew Gallatin if (hdr == NULL) { 7094da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 7104da0d523SAndrew Gallatin return ENOMEM; 7114da0d523SAndrew Gallatin } 7124da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 7134da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 7144da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 7154da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 7164da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 717b824b7d8SAndrew Gallatin 718b824b7d8SAndrew Gallatin /* 719b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 720b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 721b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 722b824b7d8SAndrew Gallatin */ 723b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 724b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 725b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 726b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 727b824b7d8SAndrew Gallatin "working around rx filter bug\n", 728b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 729b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 730b824b7d8SAndrew Gallatin } 731b824b7d8SAndrew Gallatin 7324da0d523SAndrew Gallatin return status; 7334da0d523SAndrew Gallatin } 7344da0d523SAndrew Gallatin 735b2fc195eSAndrew Gallatin 736b2fc195eSAndrew Gallatin static int 7376d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 738b2fc195eSAndrew Gallatin { 739b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 740b2fc195eSAndrew Gallatin volatile char *submit; 741b2fc195eSAndrew Gallatin char buf_bytes[72]; 742b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 743b2fc195eSAndrew Gallatin int status, i; 744b2fc195eSAndrew Gallatin 745b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 746b2fc195eSAndrew Gallatin 747b2fc195eSAndrew Gallatin size = sc->sram_size; 7486d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 749b2fc195eSAndrew Gallatin if (status) { 7504da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 7514da0d523SAndrew Gallatin it is new enough */ 7524da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 7534da0d523SAndrew Gallatin if (status) { 7544da0d523SAndrew Gallatin device_printf(sc->dev, 7554da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 756b2fc195eSAndrew Gallatin return status; 757b2fc195eSAndrew Gallatin } 7584da0d523SAndrew Gallatin device_printf(sc->dev, 7594da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 7604da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 7614da0d523SAndrew Gallatin device_printf(sc->dev, 7624da0d523SAndrew Gallatin "Using firmware currently running on NIC" 7634da0d523SAndrew Gallatin ". For optimal\n"); 7644da0d523SAndrew Gallatin device_printf(sc->dev, 7654da0d523SAndrew Gallatin "performance consider loading optimized " 7664da0d523SAndrew Gallatin "firmware\n"); 7674da0d523SAndrew Gallatin } 768d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 769d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 770d91b1b49SAndrew Gallatin return 0; 7714da0d523SAndrew Gallatin } 772b2fc195eSAndrew Gallatin /* clear confirmation addr */ 773b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 774b2fc195eSAndrew Gallatin *confirm = 0; 775b2fc195eSAndrew Gallatin mb(); 776b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 777b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 778b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 779b2fc195eSAndrew Gallatin */ 780b2fc195eSAndrew Gallatin 7816d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7826d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 783b2fc195eSAndrew Gallatin 784b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 785b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 786b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 787b2fc195eSAndrew Gallatin 788b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 789b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 790b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 791b2fc195eSAndrew Gallatin */ 792b2fc195eSAndrew Gallatin /* where the code starts*/ 7936d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 794b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 795b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 796b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 797b2fc195eSAndrew Gallatin 7980fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 7996d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 800b2fc195eSAndrew Gallatin mb(); 801b2fc195eSAndrew Gallatin DELAY(1000); 802b2fc195eSAndrew Gallatin mb(); 803b2fc195eSAndrew Gallatin i = 0; 804b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 805b2fc195eSAndrew Gallatin DELAY(1000*10); 806b2fc195eSAndrew Gallatin i++; 807b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 808b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 809b2fc195eSAndrew Gallatin } 810b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 811b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 812b2fc195eSAndrew Gallatin confirm, *confirm); 813b2fc195eSAndrew Gallatin 814b2fc195eSAndrew Gallatin return ENXIO; 815b2fc195eSAndrew Gallatin } 816b2fc195eSAndrew Gallatin return 0; 817b2fc195eSAndrew Gallatin } 818b2fc195eSAndrew Gallatin 819b2fc195eSAndrew Gallatin static int 8206d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 821b2fc195eSAndrew Gallatin { 8226d87a65dSAndrew Gallatin mxge_cmd_t cmd; 823b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 824b2fc195eSAndrew Gallatin int status; 825b2fc195eSAndrew Gallatin 826b2fc195eSAndrew Gallatin 827b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 828b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 829b2fc195eSAndrew Gallatin 830b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 831b2fc195eSAndrew Gallatin 8325e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 833b2fc195eSAndrew Gallatin return status; 834b2fc195eSAndrew Gallatin } 835b2fc195eSAndrew Gallatin 836b2fc195eSAndrew Gallatin static int 8376d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 838b2fc195eSAndrew Gallatin { 8396d87a65dSAndrew Gallatin mxge_cmd_t cmd; 840b2fc195eSAndrew Gallatin int status; 841b2fc195eSAndrew Gallatin 842b2fc195eSAndrew Gallatin if (pause) 8435e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 844b2fc195eSAndrew Gallatin &cmd); 845b2fc195eSAndrew Gallatin else 8465e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 847b2fc195eSAndrew Gallatin &cmd); 848b2fc195eSAndrew Gallatin 849b2fc195eSAndrew Gallatin if (status) { 850b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 851b2fc195eSAndrew Gallatin return ENXIO; 852b2fc195eSAndrew Gallatin } 853b2fc195eSAndrew Gallatin sc->pause = pause; 854b2fc195eSAndrew Gallatin return 0; 855b2fc195eSAndrew Gallatin } 856b2fc195eSAndrew Gallatin 857b2fc195eSAndrew Gallatin static void 8586d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 859b2fc195eSAndrew Gallatin { 8606d87a65dSAndrew Gallatin mxge_cmd_t cmd; 861b2fc195eSAndrew Gallatin int status; 862b2fc195eSAndrew Gallatin 863b2fc195eSAndrew Gallatin if (promisc) 8645e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 865b2fc195eSAndrew Gallatin &cmd); 866b2fc195eSAndrew Gallatin else 8675e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 868b2fc195eSAndrew Gallatin &cmd); 869b2fc195eSAndrew Gallatin 870b2fc195eSAndrew Gallatin if (status) { 871b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 872b2fc195eSAndrew Gallatin } 873b2fc195eSAndrew Gallatin } 874b2fc195eSAndrew Gallatin 8750fa7f681SAndrew Gallatin static void 8760fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 8770fa7f681SAndrew Gallatin { 8780fa7f681SAndrew Gallatin mxge_cmd_t cmd; 8790fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 8800fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 8810fa7f681SAndrew Gallatin int err; 8820fa7f681SAndrew Gallatin 8830fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 8840fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 8850fa7f681SAndrew Gallatin return; 8860fa7f681SAndrew Gallatin 8870fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 8880fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 8890fa7f681SAndrew Gallatin if (err != 0) { 8900fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 8910fa7f681SAndrew Gallatin " error status: %d\n", err); 8920fa7f681SAndrew Gallatin return; 8930fa7f681SAndrew Gallatin } 8940fa7f681SAndrew Gallatin 895b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 896b824b7d8SAndrew Gallatin return; 8970fa7f681SAndrew Gallatin 8980fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 8990fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 9000fa7f681SAndrew Gallatin return; 9010fa7f681SAndrew Gallatin 9020fa7f681SAndrew Gallatin /* Flush all the filters */ 9030fa7f681SAndrew Gallatin 9040fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 9050fa7f681SAndrew Gallatin if (err != 0) { 9060fa7f681SAndrew Gallatin device_printf(sc->dev, 9070fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 9080fa7f681SAndrew Gallatin ", error status: %d\n", err); 9090fa7f681SAndrew Gallatin return; 9100fa7f681SAndrew Gallatin } 9110fa7f681SAndrew Gallatin 9120fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 9130fa7f681SAndrew Gallatin 9140fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 9150fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 9160fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 9170fa7f681SAndrew Gallatin continue; 9180fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 9190fa7f681SAndrew Gallatin &cmd.data0, 4); 9200fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 9210fa7f681SAndrew Gallatin &cmd.data1, 2); 9220fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 9230fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 9240fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 9250fa7f681SAndrew Gallatin if (err != 0) { 9260fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 9270fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 9280fa7f681SAndrew Gallatin "%d\t", err); 9290fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 9300fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9310fa7f681SAndrew Gallatin return; 9320fa7f681SAndrew Gallatin } 9330fa7f681SAndrew Gallatin } 9340fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9350fa7f681SAndrew Gallatin /* Enable multicast filtering */ 9360fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 9370fa7f681SAndrew Gallatin if (err != 0) { 9380fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 9390fa7f681SAndrew Gallatin ", error status: %d\n", err); 9400fa7f681SAndrew Gallatin } 9410fa7f681SAndrew Gallatin } 9420fa7f681SAndrew Gallatin 9430fa7f681SAndrew Gallatin 944b2fc195eSAndrew Gallatin static int 9456d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc) 946b2fc195eSAndrew Gallatin { 947b2fc195eSAndrew Gallatin 9486d87a65dSAndrew Gallatin mxge_cmd_t cmd; 9495e7d8541SAndrew Gallatin size_t bytes; 9505e7d8541SAndrew Gallatin int status; 951b2fc195eSAndrew Gallatin 952b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 953b2fc195eSAndrew Gallatin is alive */ 954b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 9555e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 956b2fc195eSAndrew Gallatin if (status != 0) { 957b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 958b2fc195eSAndrew Gallatin return ENXIO; 959b2fc195eSAndrew Gallatin } 960b2fc195eSAndrew Gallatin 961091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 962091feecdSAndrew Gallatin 963b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 9645e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\ 9655e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 9665e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 9675e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 9685e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 9695e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 9705e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 971b2fc195eSAndrew Gallatin 9726d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 9735e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 9745e7d8541SAndrew Gallatin 9755e7d8541SAndrew Gallatin 9765e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 9775e7d8541SAndrew Gallatin 9785e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 9795e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 9805e7d8541SAndrew Gallatin 9815e7d8541SAndrew Gallatin 9825e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 9836d87a65dSAndrew Gallatin &cmd); 9845e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 985b2fc195eSAndrew Gallatin if (status != 0) { 986b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 987b2fc195eSAndrew Gallatin return status; 988b2fc195eSAndrew Gallatin } 989b2fc195eSAndrew Gallatin 9905e7d8541SAndrew Gallatin 9915e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 9925e7d8541SAndrew Gallatin 9935e7d8541SAndrew Gallatin 9945e7d8541SAndrew Gallatin /* run a DMA benchmark */ 9955e7d8541SAndrew Gallatin sc->read_dma = sc->write_dma = sc->read_write_dma = 0; 9965e7d8541SAndrew Gallatin 9975e7d8541SAndrew Gallatin /* Read DMA */ 998a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 999a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10005e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10000; 10015e7d8541SAndrew Gallatin 10025e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10035e7d8541SAndrew Gallatin if (status != 0) 10045e7d8541SAndrew Gallatin device_printf(sc->dev, "read dma benchmark failed\n"); 10055e7d8541SAndrew Gallatin else 10065e7d8541SAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 10075e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10085e7d8541SAndrew Gallatin 10095e7d8541SAndrew Gallatin /* Write DMA */ 1010a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 1011a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10125e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x1; 10135e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10145e7d8541SAndrew Gallatin if (status != 0) 10155e7d8541SAndrew Gallatin device_printf(sc->dev, "write dma benchmark failed\n"); 10165e7d8541SAndrew Gallatin else 10175e7d8541SAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 10185e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10195e7d8541SAndrew Gallatin /* Read/Write DMA */ 1020a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 1021a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10225e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10001; 10235e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10245e7d8541SAndrew Gallatin if (status != 0) 10255e7d8541SAndrew Gallatin device_printf(sc->dev, "read/write dma benchmark failed\n"); 10265e7d8541SAndrew Gallatin else 10275e7d8541SAndrew Gallatin sc->read_write_dma = 10285e7d8541SAndrew Gallatin ((cmd.data0>>16) * sc->tx.boundary * 2 * 2) / 10295e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10305e7d8541SAndrew Gallatin 1031b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 10325e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 10335e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 10345e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 1035b2fc195eSAndrew Gallatin sc->tx.req = 0; 1036b2fc195eSAndrew Gallatin sc->tx.done = 0; 10375e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 1038a82c2581SAndrew Gallatin sc->tx.wake = 0; 1039a82c2581SAndrew Gallatin sc->tx.stall = 0; 1040b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1041b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1042b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 1043a98d6cd7SAndrew Gallatin sc->fw_stats->valid = 0; 1044a98d6cd7SAndrew Gallatin sc->fw_stats->send_done_count = 0; 10456d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 10466d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 10476d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 10480fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1049b2fc195eSAndrew Gallatin return status; 1050b2fc195eSAndrew Gallatin } 1051b2fc195eSAndrew Gallatin 1052b2fc195eSAndrew Gallatin static int 10536d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1054b2fc195eSAndrew Gallatin { 10556d87a65dSAndrew Gallatin mxge_softc_t *sc; 1056b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1057b2fc195eSAndrew Gallatin int err; 1058b2fc195eSAndrew Gallatin 1059b2fc195eSAndrew Gallatin sc = arg1; 1060b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1061b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1062b2fc195eSAndrew Gallatin if (err != 0) { 1063b2fc195eSAndrew Gallatin return err; 1064b2fc195eSAndrew Gallatin } 1065b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1066b2fc195eSAndrew Gallatin return 0; 1067b2fc195eSAndrew Gallatin 1068b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1069b2fc195eSAndrew Gallatin return EINVAL; 1070b2fc195eSAndrew Gallatin 1071a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 10725e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1073b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 10745e7d8541SAndrew Gallatin 1075a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1076b2fc195eSAndrew Gallatin return err; 1077b2fc195eSAndrew Gallatin } 1078b2fc195eSAndrew Gallatin 1079b2fc195eSAndrew Gallatin static int 10806d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1081b2fc195eSAndrew Gallatin { 10826d87a65dSAndrew Gallatin mxge_softc_t *sc; 1083b2fc195eSAndrew Gallatin unsigned int enabled; 1084b2fc195eSAndrew Gallatin int err; 1085b2fc195eSAndrew Gallatin 1086b2fc195eSAndrew Gallatin sc = arg1; 1087b2fc195eSAndrew Gallatin enabled = sc->pause; 1088b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1089b2fc195eSAndrew Gallatin if (err != 0) { 1090b2fc195eSAndrew Gallatin return err; 1091b2fc195eSAndrew Gallatin } 1092b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1093b2fc195eSAndrew Gallatin return 0; 1094b2fc195eSAndrew Gallatin 1095a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 10966d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1097a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1098b2fc195eSAndrew Gallatin return err; 1099b2fc195eSAndrew Gallatin } 1100b2fc195eSAndrew Gallatin 1101b2fc195eSAndrew Gallatin static int 11026d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1103b2fc195eSAndrew Gallatin { 1104b2fc195eSAndrew Gallatin int err; 1105b2fc195eSAndrew Gallatin 1106b2fc195eSAndrew Gallatin if (arg1 == NULL) 1107b2fc195eSAndrew Gallatin return EFAULT; 1108b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1109b2fc195eSAndrew Gallatin arg1 = NULL; 1110b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1111b2fc195eSAndrew Gallatin 1112b2fc195eSAndrew Gallatin return err; 1113b2fc195eSAndrew Gallatin } 1114b2fc195eSAndrew Gallatin 1115b2fc195eSAndrew Gallatin static void 11166d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1117b2fc195eSAndrew Gallatin { 1118b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1119b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 11205e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1121b2fc195eSAndrew Gallatin 1122b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1123b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1124b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1125b2fc195eSAndrew Gallatin 11265e7d8541SAndrew Gallatin /* random information */ 11275e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11285e7d8541SAndrew Gallatin "firmware_version", 11295e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 11305e7d8541SAndrew Gallatin 0, "firmware version"); 11315e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11325e7d8541SAndrew Gallatin "serial_number", 11335e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 11345e7d8541SAndrew Gallatin 0, "serial number"); 11355e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11365e7d8541SAndrew Gallatin "product_code", 11375e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 11385e7d8541SAndrew Gallatin 0, "product_code"); 11395e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1140d91b1b49SAndrew Gallatin "pcie_link_width", 1141d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1142d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1143d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11445e7d8541SAndrew Gallatin "tx_boundary", 11455e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 11465e7d8541SAndrew Gallatin 0, "tx_boundary"); 11475e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1148091feecdSAndrew Gallatin "write_combine", 1149091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1150091feecdSAndrew Gallatin 0, "write combining PIO?"); 1151091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11525e7d8541SAndrew Gallatin "read_dma_MBs", 11535e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 11545e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 11555e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11565e7d8541SAndrew Gallatin "write_dma_MBs", 11575e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 11585e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 11595e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11605e7d8541SAndrew Gallatin "read_write_dma_MBs", 11615e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 11625e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 11635e7d8541SAndrew Gallatin 11645e7d8541SAndrew Gallatin 11655e7d8541SAndrew Gallatin /* performance related tunables */ 1166b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1167b2fc195eSAndrew Gallatin "intr_coal_delay", 1168b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11696d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1170b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1171b2fc195eSAndrew Gallatin 1172b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1173b2fc195eSAndrew Gallatin "flow_control_enabled", 1174b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11756d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1176b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1177b2fc195eSAndrew Gallatin 1178b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11795e7d8541SAndrew Gallatin "deassert_wait", 11805e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 11815e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1182b2fc195eSAndrew Gallatin 1183b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1184b2fc195eSAndrew Gallatin Need to swap it */ 1185b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1186b2fc195eSAndrew Gallatin "link_up", 1187b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 11886d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1189b2fc195eSAndrew Gallatin "I", "link up"); 1190b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1191b2fc195eSAndrew Gallatin "rdma_tags_available", 1192b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 11936d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1194b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1195b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1196b2fc195eSAndrew Gallatin "dropped_link_overflow", 1197b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 11986d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1199b2fc195eSAndrew Gallatin "I", "dropped_link_overflow"); 1200b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1201b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1202b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1203b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 12046d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1205b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1206b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 12070fa7f681SAndrew Gallatin "dropped_multicast_filtered", 12080fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 12090fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 12100fa7f681SAndrew Gallatin 0, mxge_handle_be32, 12110fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 12120fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1213b2fc195eSAndrew Gallatin "dropped_runt", 1214b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 12156d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1216b2fc195eSAndrew Gallatin "I", "dropped_runt"); 1217b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1218b2fc195eSAndrew Gallatin "dropped_overrun", 1219b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 12206d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1221b2fc195eSAndrew Gallatin "I", "dropped_overrun"); 1222b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1223b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1224b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1225b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 12266d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1227b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1228b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1229b2fc195eSAndrew Gallatin "dropped_no_big_buffer", 1230b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 12316d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1232b2fc195eSAndrew Gallatin "I", "dropped_no_big_buffer"); 1233b2fc195eSAndrew Gallatin 1234b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1235b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12365e7d8541SAndrew Gallatin "rx_small_cnt", 12375e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 12385e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12395e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12405e7d8541SAndrew Gallatin "rx_big_cnt", 12415e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 12425e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12435e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1244b2fc195eSAndrew Gallatin "tx_req", 1245b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1246b2fc195eSAndrew Gallatin 0, "tx_req"); 1247b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1248b2fc195eSAndrew Gallatin "tx_done", 1249b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1250b2fc195eSAndrew Gallatin 0, "tx_done"); 1251b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12525e7d8541SAndrew Gallatin "tx_pkt_done", 12535e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 12545e7d8541SAndrew Gallatin 0, "tx_done"); 1255a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1256a82c2581SAndrew Gallatin "tx_stall", 1257a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.stall, 1258a82c2581SAndrew Gallatin 0, "tx_stall"); 1259a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1260a82c2581SAndrew Gallatin "tx_wake", 1261a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.wake, 1262a82c2581SAndrew Gallatin 0, "tx_wake"); 12635e7d8541SAndrew Gallatin 12645e7d8541SAndrew Gallatin /* verbose printing? */ 1265b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12665e7d8541SAndrew Gallatin "verbose", 12675e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 12685e7d8541SAndrew Gallatin 0, "verbose printing"); 1269b2fc195eSAndrew Gallatin 1270b2fc195eSAndrew Gallatin } 1271b2fc195eSAndrew Gallatin 1272b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1273b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1274b2fc195eSAndrew Gallatin 1275b2fc195eSAndrew Gallatin static inline void 12766d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1277b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1278b2fc195eSAndrew Gallatin { 1279b2fc195eSAndrew Gallatin int idx, starting_slot; 1280b2fc195eSAndrew Gallatin starting_slot = tx->req; 1281b2fc195eSAndrew Gallatin while (cnt > 1) { 1282b2fc195eSAndrew Gallatin cnt--; 1283b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 12846d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1285b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1286b2fc195eSAndrew Gallatin mb(); 1287b2fc195eSAndrew Gallatin } 1288b2fc195eSAndrew Gallatin } 1289b2fc195eSAndrew Gallatin 1290b2fc195eSAndrew Gallatin /* 1291b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1292b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1293b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1294b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1295b2fc195eSAndrew Gallatin */ 1296b2fc195eSAndrew Gallatin 1297b2fc195eSAndrew Gallatin static inline void 12986d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1299b2fc195eSAndrew Gallatin int cnt) 1300b2fc195eSAndrew Gallatin { 1301b2fc195eSAndrew Gallatin int idx, i; 1302b2fc195eSAndrew Gallatin uint32_t *src_ints; 1303b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1304b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1305b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 13065e7d8541SAndrew Gallatin uint8_t last_flags; 1307b2fc195eSAndrew Gallatin 1308b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1309b2fc195eSAndrew Gallatin 13105e7d8541SAndrew Gallatin last_flags = src->flags; 13115e7d8541SAndrew Gallatin src->flags = 0; 1312b2fc195eSAndrew Gallatin mb(); 1313b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1314b2fc195eSAndrew Gallatin srcp = src; 1315b2fc195eSAndrew Gallatin 1316b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1317b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 13186d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1319b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1320b2fc195eSAndrew Gallatin srcp += 2; 1321b2fc195eSAndrew Gallatin dstp += 2; 1322b2fc195eSAndrew Gallatin } 1323b2fc195eSAndrew Gallatin } else { 1324b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1325b2fc195eSAndrew Gallatin that it is submitted below */ 13266d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1327b2fc195eSAndrew Gallatin i = 0; 1328b2fc195eSAndrew Gallatin } 1329b2fc195eSAndrew Gallatin if (i < cnt) { 1330b2fc195eSAndrew Gallatin /* submit the first request */ 13316d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1332b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1333b2fc195eSAndrew Gallatin } 1334b2fc195eSAndrew Gallatin 1335b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 13365e7d8541SAndrew Gallatin src->flags = last_flags; 1337b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1338b2fc195eSAndrew Gallatin src_ints+=3; 1339b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1340b2fc195eSAndrew Gallatin dst_ints+=3; 1341b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1342b2fc195eSAndrew Gallatin tx->req += cnt; 1343b2fc195eSAndrew Gallatin mb(); 1344b2fc195eSAndrew Gallatin } 1345b2fc195eSAndrew Gallatin 1346b2fc195eSAndrew Gallatin static inline void 13476d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt) 1348b2fc195eSAndrew Gallatin { 1349b2fc195eSAndrew Gallatin tx->req += cnt; 1350b2fc195eSAndrew Gallatin mb(); 1351b2fc195eSAndrew Gallatin while (cnt >= 4) { 13526d87a65dSAndrew Gallatin mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64); 1353b2fc195eSAndrew Gallatin mb(); 1354b2fc195eSAndrew Gallatin src += 4; 1355b2fc195eSAndrew Gallatin cnt -= 4; 1356b2fc195eSAndrew Gallatin } 1357b2fc195eSAndrew Gallatin if (cnt > 0) { 1358b2fc195eSAndrew Gallatin /* pad it to 64 bytes. The src is 64 bytes bigger than it 1359b2fc195eSAndrew Gallatin needs to be so that we don't overrun it */ 13600fa7f681SAndrew Gallatin mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64); 1361b2fc195eSAndrew Gallatin mb(); 1362b2fc195eSAndrew Gallatin } 1363b2fc195eSAndrew Gallatin } 1364b2fc195eSAndrew Gallatin 1365b2fc195eSAndrew Gallatin static void 1366aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt) 1367aed8e389SAndrew Gallatin { 1368aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1369aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1370aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1371aed8e389SAndrew Gallatin struct ether_header *eh; 1372aed8e389SAndrew Gallatin struct ip *ip; 1373aed8e389SAndrew Gallatin struct tcphdr *tcp; 1374aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1375aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1376aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1377aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1378aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1379aed8e389SAndrew Gallatin static int once; 1380aed8e389SAndrew Gallatin 1381aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1382aed8e389SAndrew Gallatin 1383aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1384aed8e389SAndrew Gallatin * send loop that we are still in the 1385aed8e389SAndrew Gallatin * header portion of the TSO packet. 1386aed8e389SAndrew Gallatin */ 1387aed8e389SAndrew Gallatin 1388aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1389aed8e389SAndrew Gallatin header together in the first mbuf, copy 1390aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1391aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1392aed8e389SAndrew Gallatin + sizeof (*ip))) { 1393aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1394aed8e389SAndrew Gallatin sc->scratch); 1395aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1396aed8e389SAndrew Gallatin } else { 1397aed8e389SAndrew Gallatin eh = mtod(m, struct ether_header *); 1398aed8e389SAndrew Gallatin } 1399aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1400aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2) 1401aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1402aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2) 1403aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1404aed8e389SAndrew Gallatin eh = (struct ether_header *) sc->scratch; 1405aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1406aed8e389SAndrew Gallatin } 1407aed8e389SAndrew Gallatin 1408aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1409aed8e389SAndrew Gallatin cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2)); 1410aed8e389SAndrew Gallatin 1411aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1412aed8e389SAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1413aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1414aed8e389SAndrew Gallatin 1415aed8e389SAndrew Gallatin 1416aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1417aed8e389SAndrew Gallatin * The firmware figures out where to put 1418aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1419aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1420aed8e389SAndrew Gallatin 1421aed8e389SAndrew Gallatin tx = &sc->tx; 1422aed8e389SAndrew Gallatin req = tx->req_list; 1423aed8e389SAndrew Gallatin seg = tx->seg_list; 1424aed8e389SAndrew Gallatin cnt = 0; 1425aed8e389SAndrew Gallatin rdma_count = 0; 1426aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1427aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1428aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1429aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1430aed8e389SAndrew Gallatin * to 0 after a segment cut. 1431aed8e389SAndrew Gallatin * 1432aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1433aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1434aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1435aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1436aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1437aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1438aed8e389SAndrew Gallatin * 1439aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1440aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1441aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1442aed8e389SAndrew Gallatin */ 1443aed8e389SAndrew Gallatin 1444aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1445aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1446aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1447aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1448aed8e389SAndrew Gallatin len = seglen = seg->ds_len; 1449aed8e389SAndrew Gallatin 1450aed8e389SAndrew Gallatin while (len) { 1451aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1452aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1453aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1454aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1455aed8e389SAndrew Gallatin /* payload */ 1456aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1457aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1458aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1459aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1460aed8e389SAndrew Gallatin flags_next |= next_is_first * 1461aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1462aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1463aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1464aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1465aed8e389SAndrew Gallatin /* header ends */ 1466aed8e389SAndrew Gallatin rdma_count = -1; 1467aed8e389SAndrew Gallatin cum_len_next = 0; 1468aed8e389SAndrew Gallatin seglen = -cum_len; 1469aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1470aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1471aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1472aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1473aed8e389SAndrew Gallatin } 1474aed8e389SAndrew Gallatin 1475aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1476aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1477aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1478aed8e389SAndrew Gallatin req->pad = 0; 1479aed8e389SAndrew Gallatin req->rdma_count = 1; 1480aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1481aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1482aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1483aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1484aed8e389SAndrew Gallatin low += seglen; 1485aed8e389SAndrew Gallatin len -= seglen; 1486aed8e389SAndrew Gallatin cum_len = cum_len_next; 1487aed8e389SAndrew Gallatin flags = flags_next; 1488aed8e389SAndrew Gallatin req++; 1489aed8e389SAndrew Gallatin cnt++; 1490aed8e389SAndrew Gallatin rdma_count++; 1491aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1492aed8e389SAndrew Gallatin cksum_offset -= seglen; 1493aed8e389SAndrew Gallatin else 1494aed8e389SAndrew Gallatin cksum_offset = 0; 1495aed8e389SAndrew Gallatin if (__predict_false(cnt > MXGE_MAX_SEND_DESC)) 1496aed8e389SAndrew Gallatin goto drop; 1497aed8e389SAndrew Gallatin } 1498aed8e389SAndrew Gallatin busdma_seg_cnt--; 1499aed8e389SAndrew Gallatin seg++; 1500aed8e389SAndrew Gallatin } 1501aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1502aed8e389SAndrew Gallatin 1503aed8e389SAndrew Gallatin do { 1504aed8e389SAndrew Gallatin req--; 1505aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1506aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1507aed8e389SAndrew Gallatin 1508aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1509aed8e389SAndrew Gallatin if (tx->wc_fifo == NULL) 1510aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1511aed8e389SAndrew Gallatin else 1512aed8e389SAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1513aed8e389SAndrew Gallatin return; 1514aed8e389SAndrew Gallatin 1515aed8e389SAndrew Gallatin drop: 1516aed8e389SAndrew Gallatin m_freem(m); 1517aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1518aed8e389SAndrew Gallatin if (!once) { 1519aed8e389SAndrew Gallatin printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n"); 1520aed8e389SAndrew Gallatin printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list); 1521aed8e389SAndrew Gallatin once = 1; 1522aed8e389SAndrew Gallatin } 1523aed8e389SAndrew Gallatin return; 1524aed8e389SAndrew Gallatin 1525aed8e389SAndrew Gallatin } 1526aed8e389SAndrew Gallatin 1527aed8e389SAndrew Gallatin static void 15286d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1529b2fc195eSAndrew Gallatin { 1530b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1531b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1532b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1533b2fc195eSAndrew Gallatin struct ifnet *ifp; 15346d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1535b2fc195eSAndrew Gallatin struct ether_header *eh; 1536b2fc195eSAndrew Gallatin struct ip *ip; 1537aed8e389SAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag; 1538aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1539aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1540b2fc195eSAndrew Gallatin 1541b2fc195eSAndrew Gallatin 1542b2fc195eSAndrew Gallatin 1543b2fc195eSAndrew Gallatin ifp = sc->ifp; 1544b2fc195eSAndrew Gallatin tx = &sc->tx; 1545b2fc195eSAndrew Gallatin 1546b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1547b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1548b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1549aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1550b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1551b2fc195eSAndrew Gallatin if (err == EFBIG) { 1552b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1553b2fc195eSAndrew Gallatin to defrag */ 1554b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1555b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1556b2fc195eSAndrew Gallatin goto drop; 1557b2fc195eSAndrew Gallatin } 1558b2fc195eSAndrew Gallatin m = m_tmp; 1559b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1560b2fc195eSAndrew Gallatin tx->info[idx].map, 1561aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1562b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1563b2fc195eSAndrew Gallatin } 1564b2fc195eSAndrew Gallatin if (err != 0) { 1565aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1566aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1567b2fc195eSAndrew Gallatin goto drop; 1568b2fc195eSAndrew Gallatin } 1569b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1570b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 15715e7d8541SAndrew Gallatin tx->info[idx].m = m; 1572b2fc195eSAndrew Gallatin 1573aed8e389SAndrew Gallatin 1574aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1575aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1576aed8e389SAndrew Gallatin mxge_encap_tso(sc, m, cnt); 1577aed8e389SAndrew Gallatin return; 1578aed8e389SAndrew Gallatin } 1579aed8e389SAndrew Gallatin 1580b2fc195eSAndrew Gallatin req = tx->req_list; 1581b2fc195eSAndrew Gallatin cksum_offset = 0; 15825e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 15835e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1584b2fc195eSAndrew Gallatin 1585b2fc195eSAndrew Gallatin /* checksum offloading? */ 1586b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1587aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1588aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1589aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1590aed8e389SAndrew Gallatin + sizeof (*ip))) { 1591aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1592aed8e389SAndrew Gallatin sc->scratch); 1593aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1594aed8e389SAndrew Gallatin } else { 1595b2fc195eSAndrew Gallatin eh = mtod(m, struct ether_header *); 1596aed8e389SAndrew Gallatin } 1597b2fc195eSAndrew Gallatin ip = (struct ip *) (eh + 1); 1598b2fc195eSAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1599b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 16005e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1601b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 16025e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1603aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1604aed8e389SAndrew Gallatin } else { 1605aed8e389SAndrew Gallatin odd_flag = 0; 1606b2fc195eSAndrew Gallatin } 16075e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 16085e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1609b2fc195eSAndrew Gallatin 1610b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1611b2fc195eSAndrew Gallatin cum_len = 0; 1612aed8e389SAndrew Gallatin seg = tx->seg_list; 16135e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1614b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1615b2fc195eSAndrew Gallatin req->addr_low = 16166d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1617b2fc195eSAndrew Gallatin req->addr_high = 16186d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1619b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1620b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1621b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1622b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1623b2fc195eSAndrew Gallatin else 1624b2fc195eSAndrew Gallatin cksum_offset = 0; 16255e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16265e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16275e7d8541SAndrew Gallatin req->rdma_count = 1; 1628aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1629b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1630b2fc195eSAndrew Gallatin seg++; 1631b2fc195eSAndrew Gallatin req++; 1632b2fc195eSAndrew Gallatin req->flags = 0; 1633b2fc195eSAndrew Gallatin } 1634b2fc195eSAndrew Gallatin req--; 1635b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1636b2fc195eSAndrew Gallatin if (cum_len < 60) { 1637b2fc195eSAndrew Gallatin req++; 1638b2fc195eSAndrew Gallatin req->addr_low = 16396d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1640b2fc195eSAndrew Gallatin req->addr_high = 16416d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1642b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 16435e7d8541SAndrew Gallatin req->cksum_offset = 0; 16445e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16455e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16465e7d8541SAndrew Gallatin req->rdma_count = 1; 1647aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1648b2fc195eSAndrew Gallatin cnt++; 1649b2fc195eSAndrew Gallatin } 16505e7d8541SAndrew Gallatin 16515e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 16525e7d8541SAndrew Gallatin #if 0 16535e7d8541SAndrew Gallatin /* print what the firmware will see */ 16545e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 16555e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 16565e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 16575e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 16585e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 16595e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 16605e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 16615e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 16625e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 16635e7d8541SAndrew Gallatin } 16645e7d8541SAndrew Gallatin printf("--------------\n"); 16655e7d8541SAndrew Gallatin #endif 16665e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1667b2fc195eSAndrew Gallatin if (tx->wc_fifo == NULL) 16686d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1669b2fc195eSAndrew Gallatin else 16706d87a65dSAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1671b2fc195eSAndrew Gallatin return; 1672b2fc195eSAndrew Gallatin 1673b2fc195eSAndrew Gallatin drop: 1674b2fc195eSAndrew Gallatin m_freem(m); 1675b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1676b2fc195eSAndrew Gallatin return; 1677b2fc195eSAndrew Gallatin } 1678b2fc195eSAndrew Gallatin 1679b2fc195eSAndrew Gallatin 16806d914a32SAndrew Gallatin 16816d914a32SAndrew Gallatin 16826d914a32SAndrew Gallatin static inline void 16836d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1684b2fc195eSAndrew Gallatin { 1685b2fc195eSAndrew Gallatin struct mbuf *m; 1686b2fc195eSAndrew Gallatin struct ifnet *ifp; 1687b2fc195eSAndrew Gallatin 1688b2fc195eSAndrew Gallatin ifp = sc->ifp; 16896d914a32SAndrew Gallatin while ((sc->tx.mask - (sc->tx.req - sc->tx.done)) 16906d914a32SAndrew Gallatin > MXGE_MAX_SEND_DESC) { 1691b2fc195eSAndrew Gallatin 16926d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 16936d914a32SAndrew Gallatin if (m == NULL) { 16946d914a32SAndrew Gallatin return; 16956d914a32SAndrew Gallatin } 1696b2fc195eSAndrew Gallatin /* let BPF see it */ 1697b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1698b2fc195eSAndrew Gallatin 1699b2fc195eSAndrew Gallatin /* give it to the nic */ 17006d87a65dSAndrew Gallatin mxge_encap(sc, m); 17016d914a32SAndrew Gallatin } 17026d914a32SAndrew Gallatin /* ran out of transmit slots */ 1703a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 1704b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1705a82c2581SAndrew Gallatin sc->tx.stall++; 1706a82c2581SAndrew Gallatin } 1707b2fc195eSAndrew Gallatin } 1708b2fc195eSAndrew Gallatin 1709b2fc195eSAndrew Gallatin static void 17106d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1711b2fc195eSAndrew Gallatin { 17126d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1713b2fc195eSAndrew Gallatin 1714b2fc195eSAndrew Gallatin 1715a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 17166d87a65dSAndrew Gallatin mxge_start_locked(sc); 1717a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 1718b2fc195eSAndrew Gallatin } 1719b2fc195eSAndrew Gallatin 17205e7d8541SAndrew Gallatin /* 17215e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 17225e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 17235e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 17245e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 17255e7d8541SAndrew Gallatin * in a burst 17265e7d8541SAndrew Gallatin */ 17275e7d8541SAndrew Gallatin static inline void 17285e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 17295e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 17305e7d8541SAndrew Gallatin { 17315e7d8541SAndrew Gallatin uint32_t low; 17325e7d8541SAndrew Gallatin 17335e7d8541SAndrew Gallatin low = src->addr_low; 17345e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 1735a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 1736a1480dfbSAndrew Gallatin mb(); 1737a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 17385e7d8541SAndrew Gallatin mb(); 173940385a5fSAndrew Gallatin src->addr_low = low; 17405e7d8541SAndrew Gallatin dst->addr_low = low; 17415e7d8541SAndrew Gallatin mb(); 17425e7d8541SAndrew Gallatin } 17435e7d8541SAndrew Gallatin 1744b2fc195eSAndrew Gallatin static int 17456d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1746b2fc195eSAndrew Gallatin { 1747b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1748b2fc195eSAndrew Gallatin struct mbuf *m; 17496d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1750b2fc195eSAndrew Gallatin int cnt, err; 1751b2fc195eSAndrew Gallatin 1752b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1753b2fc195eSAndrew Gallatin if (m == NULL) { 1754b2fc195eSAndrew Gallatin rx->alloc_fail++; 1755b2fc195eSAndrew Gallatin err = ENOBUFS; 1756b2fc195eSAndrew Gallatin goto done; 1757b2fc195eSAndrew Gallatin } 1758b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1759b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1760b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1761b2fc195eSAndrew Gallatin if (err != 0) { 1762b2fc195eSAndrew Gallatin m_free(m); 1763b2fc195eSAndrew Gallatin goto done; 1764b2fc195eSAndrew Gallatin } 1765b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1766b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 17676d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1768b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 17696d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1770b2fc195eSAndrew Gallatin 1771b2fc195eSAndrew Gallatin done: 1772b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 17735e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 17745e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 17755e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 17765e7d8541SAndrew Gallatin else { 1777b2fc195eSAndrew Gallatin mb(); 17785e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 17795e7d8541SAndrew Gallatin } 1780b2fc195eSAndrew Gallatin } 1781b2fc195eSAndrew Gallatin return err; 1782b2fc195eSAndrew Gallatin } 1783b2fc195eSAndrew Gallatin 1784b2fc195eSAndrew Gallatin static int 17856d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1786b2fc195eSAndrew Gallatin { 1787b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1788b2fc195eSAndrew Gallatin struct mbuf *m; 17896d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1790b2fc195eSAndrew Gallatin int cnt, err; 1791b2fc195eSAndrew Gallatin 1792b2fc195eSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes); 1793b2fc195eSAndrew Gallatin if (m == NULL) { 1794b2fc195eSAndrew Gallatin rx->alloc_fail++; 1795b2fc195eSAndrew Gallatin err = ENOBUFS; 1796b2fc195eSAndrew Gallatin goto done; 1797b2fc195eSAndrew Gallatin } 1798b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1799b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1800b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1801b2fc195eSAndrew Gallatin if (err != 0) { 1802b2fc195eSAndrew Gallatin m_free(m); 1803b2fc195eSAndrew Gallatin goto done; 1804b2fc195eSAndrew Gallatin } 1805b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1806b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 18076d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1808b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 18096d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1810b2fc195eSAndrew Gallatin 1811b2fc195eSAndrew Gallatin done: 1812b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 18135e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 18145e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 18155e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 18165e7d8541SAndrew Gallatin else { 1817b2fc195eSAndrew Gallatin mb(); 18185e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 18195e7d8541SAndrew Gallatin } 1820b2fc195eSAndrew Gallatin } 1821b2fc195eSAndrew Gallatin return err; 1822b2fc195eSAndrew Gallatin } 1823b2fc195eSAndrew Gallatin 1824b2fc195eSAndrew Gallatin static inline void 18255e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 18265e7d8541SAndrew Gallatin { 18275e7d8541SAndrew Gallatin struct ether_header *eh; 18285e7d8541SAndrew Gallatin struct ip *ip; 18295e7d8541SAndrew Gallatin 18305e7d8541SAndrew Gallatin eh = mtod(m, struct ether_header *); 18315e7d8541SAndrew Gallatin if (__predict_true(eh->ether_type == htons(ETHERTYPE_IP))) { 18325e7d8541SAndrew Gallatin ip = (struct ip *)(eh + 1); 18335e7d8541SAndrew Gallatin if (__predict_true(ip->ip_p == IPPROTO_TCP || 18345e7d8541SAndrew Gallatin ip->ip_p == IPPROTO_UDP)) { 18355e7d8541SAndrew Gallatin m->m_pkthdr.csum_data = csum; 18365e7d8541SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_DATA_VALID; 18375e7d8541SAndrew Gallatin } 18385e7d8541SAndrew Gallatin } 18395e7d8541SAndrew Gallatin } 18405e7d8541SAndrew Gallatin 18415e7d8541SAndrew Gallatin static inline void 18425e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum) 1843b2fc195eSAndrew Gallatin { 1844b2fc195eSAndrew Gallatin struct ifnet *ifp; 1845b2fc195eSAndrew Gallatin struct mbuf *m = 0; /* -Wunitialized */ 1846b2fc195eSAndrew Gallatin struct mbuf *m_prev = 0; /* -Wunitialized */ 1847b2fc195eSAndrew Gallatin struct mbuf *m_head = 0; 1848b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 18496d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1850b2fc195eSAndrew Gallatin int idx; 1851b2fc195eSAndrew Gallatin 1852b2fc195eSAndrew Gallatin 1853b2fc195eSAndrew Gallatin rx = &sc->rx_big; 1854b2fc195eSAndrew Gallatin ifp = sc->ifp; 1855b2fc195eSAndrew Gallatin while (len > 0) { 1856b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1857b2fc195eSAndrew Gallatin rx->cnt++; 1858b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1859b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1860b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 18616d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 1862b2fc195eSAndrew Gallatin goto drop; 1863b2fc195eSAndrew Gallatin } 1864b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1865b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1866b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1867b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1868b2fc195eSAndrew Gallatin 1869b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1870b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1871b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1872b2fc195eSAndrew Gallatin 1873b2fc195eSAndrew Gallatin /* chain multiple segments together */ 1874b2fc195eSAndrew Gallatin if (!m_head) { 1875b2fc195eSAndrew Gallatin m_head = m; 1876b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st bytes so that 1877b2fc195eSAndrew Gallatin * packet is properly aligned */ 18785e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1879b2fc195eSAndrew Gallatin m->m_pkthdr.len = len; 18805e7d8541SAndrew Gallatin m->m_len = sc->big_bytes - MXGEFW_PAD; 1881b2fc195eSAndrew Gallatin } else { 1882b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1883b2fc195eSAndrew Gallatin m->m_flags &= ~M_PKTHDR; 1884b2fc195eSAndrew Gallatin m_prev->m_next = m; 1885b2fc195eSAndrew Gallatin } 1886b2fc195eSAndrew Gallatin len -= m->m_len; 1887b2fc195eSAndrew Gallatin m_prev = m; 1888b2fc195eSAndrew Gallatin } 1889b2fc195eSAndrew Gallatin 1890b2fc195eSAndrew Gallatin /* trim trailing garbage from the last mbuf in the chain. If 1891b2fc195eSAndrew Gallatin * there is any garbage, len will be negative */ 1892b2fc195eSAndrew Gallatin m->m_len += len; 1893b2fc195eSAndrew Gallatin 1894b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 18955e7d8541SAndrew Gallatin if (sc->csum_flag) 18965e7d8541SAndrew Gallatin mxge_rx_csum(m_head, csum); 1897b2fc195eSAndrew Gallatin 1898b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1899b2fc195eSAndrew Gallatin m_head->m_pkthdr.rcvif = ifp; 1900b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1901b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m_head); 1902b2fc195eSAndrew Gallatin return; 1903b2fc195eSAndrew Gallatin 1904b2fc195eSAndrew Gallatin drop: 1905b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf(s) are re-cycled by running 1906b2fc195eSAndrew Gallatin every slot through the allocator */ 1907b2fc195eSAndrew Gallatin if (m_head) { 1908b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1909b2fc195eSAndrew Gallatin m_freem(m_head); 1910b2fc195eSAndrew Gallatin } else { 19115e7d8541SAndrew Gallatin len -= (sc->big_bytes + MXGEFW_PAD); 1912b2fc195eSAndrew Gallatin } 1913b2fc195eSAndrew Gallatin while ((int)len > 0) { 1914b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1915b2fc195eSAndrew Gallatin rx->cnt++; 1916b2fc195eSAndrew Gallatin m = rx->info[idx].m; 19176d87a65dSAndrew Gallatin if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) { 1918b2fc195eSAndrew Gallatin m_freem(m); 1919b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1920b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1921b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, 1922b2fc195eSAndrew Gallatin BUS_DMASYNC_POSTREAD); 1923b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1924b2fc195eSAndrew Gallatin 1925b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1926b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1927b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1928b2fc195eSAndrew Gallatin } 1929b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1930b2fc195eSAndrew Gallatin } 1931b2fc195eSAndrew Gallatin 1932b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1933b2fc195eSAndrew Gallatin 1934b2fc195eSAndrew Gallatin } 1935b2fc195eSAndrew Gallatin 1936b2fc195eSAndrew Gallatin static inline void 19375e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 1938b2fc195eSAndrew Gallatin { 1939b2fc195eSAndrew Gallatin struct ifnet *ifp; 1940b2fc195eSAndrew Gallatin struct mbuf *m; 19416d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1942b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 1943b2fc195eSAndrew Gallatin int idx; 1944b2fc195eSAndrew Gallatin 1945b2fc195eSAndrew Gallatin ifp = sc->ifp; 1946b2fc195eSAndrew Gallatin rx = &sc->rx_small; 1947b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1948b2fc195eSAndrew Gallatin rx->cnt++; 1949b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1950b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1951b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 19526d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 1953b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 1954b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1955b2fc195eSAndrew Gallatin return; 1956b2fc195eSAndrew Gallatin } 1957b2fc195eSAndrew Gallatin 1958b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1959b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1960b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1961b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1962b2fc195eSAndrew Gallatin 1963b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1964b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1965b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1966b2fc195eSAndrew Gallatin 1967b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 1968b2fc195eSAndrew Gallatin * aligned */ 19695e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1970b2fc195eSAndrew Gallatin 1971b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 19725e7d8541SAndrew Gallatin if (sc->csum_flag) 19735e7d8541SAndrew Gallatin mxge_rx_csum(m, csum); 1974b2fc195eSAndrew Gallatin 1975b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1976b2fc195eSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 1977b2fc195eSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 1978b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1979b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 1980b2fc195eSAndrew Gallatin } 1981b2fc195eSAndrew Gallatin 1982b2fc195eSAndrew Gallatin static inline void 19835e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 19845e7d8541SAndrew Gallatin { 19855e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 19865e7d8541SAndrew Gallatin int limit = 0; 19875e7d8541SAndrew Gallatin uint16_t length; 19885e7d8541SAndrew Gallatin uint16_t checksum; 19895e7d8541SAndrew Gallatin 19905e7d8541SAndrew Gallatin 19915e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 19925e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 19935e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 19945e7d8541SAndrew Gallatin checksum = ntohs(rx_done->entry[rx_done->idx].checksum); 1995b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 19965e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 19975e7d8541SAndrew Gallatin else 19985e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 19995e7d8541SAndrew Gallatin rx_done->cnt++; 20005e7d8541SAndrew Gallatin rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1); 20015e7d8541SAndrew Gallatin 20025e7d8541SAndrew Gallatin /* limit potential for livelock */ 20035e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * mxge_max_intr_slots)) 20045e7d8541SAndrew Gallatin break; 20055e7d8541SAndrew Gallatin 20065e7d8541SAndrew Gallatin } 20075e7d8541SAndrew Gallatin } 20085e7d8541SAndrew Gallatin 20095e7d8541SAndrew Gallatin 20105e7d8541SAndrew Gallatin static inline void 20116d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 2012b2fc195eSAndrew Gallatin { 2013b2fc195eSAndrew Gallatin struct ifnet *ifp; 20146d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 2015b2fc195eSAndrew Gallatin struct mbuf *m; 2016b2fc195eSAndrew Gallatin bus_dmamap_t map; 20175e7d8541SAndrew Gallatin int idx, limit; 2018b2fc195eSAndrew Gallatin 20195e7d8541SAndrew Gallatin limit = 0; 2020b2fc195eSAndrew Gallatin tx = &sc->tx; 2021b2fc195eSAndrew Gallatin ifp = sc->ifp; 20225e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2023b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2024b2fc195eSAndrew Gallatin tx->done++; 2025b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2026b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2027b2fc195eSAndrew Gallatin segment per-mbuf */ 2028b2fc195eSAndrew Gallatin if (m != NULL) { 2029b2fc195eSAndrew Gallatin ifp->if_opackets++; 2030b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2031b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2032b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2033b2fc195eSAndrew Gallatin m_freem(m); 2034b2fc195eSAndrew Gallatin } 20355e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 20365e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 20375e7d8541SAndrew Gallatin tx->pkt_done++; 20385e7d8541SAndrew Gallatin } 20395e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 20405e7d8541SAndrew Gallatin 2 full tx rings per call */ 20415e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 20425e7d8541SAndrew Gallatin break; 2043b2fc195eSAndrew Gallatin } 2044b2fc195eSAndrew Gallatin 2045b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2046b2fc195eSAndrew Gallatin its OK to send packets */ 2047b2fc195eSAndrew Gallatin 2048b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2049b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2050a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 2051b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2052a82c2581SAndrew Gallatin sc->tx.wake++; 20536d87a65dSAndrew Gallatin mxge_start_locked(sc); 2054a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 2055b2fc195eSAndrew Gallatin } 2056b2fc195eSAndrew Gallatin } 2057b2fc195eSAndrew Gallatin 2058b2fc195eSAndrew Gallatin static void 20596d87a65dSAndrew Gallatin mxge_intr(void *arg) 2060b2fc195eSAndrew Gallatin { 20616d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 20625e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 20635e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 20645e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 20655e7d8541SAndrew Gallatin uint32_t send_done_count; 20665e7d8541SAndrew Gallatin uint8_t valid; 2067b2fc195eSAndrew Gallatin 2068b2fc195eSAndrew Gallatin 20695e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 20705e7d8541SAndrew Gallatin if (!stats->valid) { 20715e7d8541SAndrew Gallatin return; 2072b2fc195eSAndrew Gallatin } 20735e7d8541SAndrew Gallatin valid = stats->valid; 2074b2fc195eSAndrew Gallatin 2075dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 20765e7d8541SAndrew Gallatin /* lower legacy IRQ */ 20775e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 20785e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 20795e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 20805e7d8541SAndrew Gallatin stats->valid = 0; 2081dc8731d4SAndrew Gallatin } else { 2082dc8731d4SAndrew Gallatin stats->valid = 0; 2083dc8731d4SAndrew Gallatin } 2084dc8731d4SAndrew Gallatin 2085dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 20865e7d8541SAndrew Gallatin do { 20875e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 20885e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 20895e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 20905e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 20915e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 20925e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 20935e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2094b2fc195eSAndrew Gallatin } 20955e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2096b2fc195eSAndrew Gallatin 20975e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 20985e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 20995e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2100b2fc195eSAndrew Gallatin if (sc->link_state) { 21015e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 21025e7d8541SAndrew Gallatin if (mxge_verbose) 21035e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2104b2fc195eSAndrew Gallatin } else { 21055e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 21065e7d8541SAndrew Gallatin if (mxge_verbose) 21075e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2108b2fc195eSAndrew Gallatin } 2109b2fc195eSAndrew Gallatin } 2110b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2111b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2112b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2113b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 21145e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 21155e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 21165e7d8541SAndrew Gallatin } 21175e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2118b2fc195eSAndrew Gallatin } 2119b2fc195eSAndrew Gallatin 21205e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 21215e7d8541SAndrew Gallatin if (valid & 0x1) 21225e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 21235e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2124b2fc195eSAndrew Gallatin } 2125b2fc195eSAndrew Gallatin 2126b2fc195eSAndrew Gallatin static void 21276d87a65dSAndrew Gallatin mxge_init(void *arg) 2128b2fc195eSAndrew Gallatin { 2129b2fc195eSAndrew Gallatin } 2130b2fc195eSAndrew Gallatin 2131b2fc195eSAndrew Gallatin 2132b2fc195eSAndrew Gallatin 2133b2fc195eSAndrew Gallatin static void 21346d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2135b2fc195eSAndrew Gallatin { 2136b2fc195eSAndrew Gallatin int i; 2137b2fc195eSAndrew Gallatin 2138b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2139b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2140b2fc195eSAndrew Gallatin continue; 2141b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2142b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2143b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2144b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2145b2fc195eSAndrew Gallatin } 2146b2fc195eSAndrew Gallatin 2147b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2148b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2149b2fc195eSAndrew Gallatin continue; 2150b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2151b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2152b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2153b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2154b2fc195eSAndrew Gallatin } 2155b2fc195eSAndrew Gallatin 2156b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2157dce01b9bSAndrew Gallatin sc->tx.info[i].flag = 0; 2158b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2159b2fc195eSAndrew Gallatin continue; 2160b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2161b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2162b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2163b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2164b2fc195eSAndrew Gallatin } 2165b2fc195eSAndrew Gallatin } 2166b2fc195eSAndrew Gallatin 2167b2fc195eSAndrew Gallatin static void 21686d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2169b2fc195eSAndrew Gallatin { 2170b2fc195eSAndrew Gallatin int i; 2171b2fc195eSAndrew Gallatin 2172aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2173b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2174aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2175aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2176b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2177b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2178b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2179b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2180b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2181c2657176SAndrew Gallatin if (sc->tx.dmat != NULL) { 2182b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2183b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2184b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2185b2fc195eSAndrew Gallatin } 2186c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2187c2657176SAndrew Gallatin } 2188b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2189b2fc195eSAndrew Gallatin } 2190b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2191c2657176SAndrew Gallatin if (sc->rx_small.dmat != NULL) { 2192b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2193b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2194b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2195b2fc195eSAndrew Gallatin } 2196c2657176SAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2197c2657176SAndrew Gallatin sc->rx_small.extra_map); 2198c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2199c2657176SAndrew Gallatin } 2200b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2201b2fc195eSAndrew Gallatin } 2202b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2203c2657176SAndrew Gallatin if (sc->rx_big.dmat != NULL) { 2204b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2205b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2206b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2207b2fc195eSAndrew Gallatin } 2208b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2209b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2210b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2211b2fc195eSAndrew Gallatin } 2212c2657176SAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2213c2657176SAndrew Gallatin } 2214c2657176SAndrew Gallatin } 2215b2fc195eSAndrew Gallatin 2216b2fc195eSAndrew Gallatin static int 22176d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2218b2fc195eSAndrew Gallatin { 22196d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2220b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2221b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2222b2fc195eSAndrew Gallatin int i, err; 2223b2fc195eSAndrew Gallatin unsigned long bytes; 2224b2fc195eSAndrew Gallatin 2225b2fc195eSAndrew Gallatin /* get ring sizes */ 22265e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2227b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 22285e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2229b2fc195eSAndrew Gallatin if (err != 0) { 2230b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2231b2fc195eSAndrew Gallatin goto abort_with_nothing; 2232b2fc195eSAndrew Gallatin } 2233b2fc195eSAndrew Gallatin 2234b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2235b2fc195eSAndrew Gallatin 2236b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2237b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 223876bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 2239a82c2581SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 224076bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2241b2fc195eSAndrew Gallatin 2242b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2243b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2244b2fc195eSAndrew Gallatin 2245b2fc195eSAndrew Gallatin err = ENOMEM; 2246b2fc195eSAndrew Gallatin 2247b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2248b2fc195eSAndrew Gallatin bytes = 8 + 22495e7d8541SAndrew Gallatin sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4); 2250b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2251b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2252b2fc195eSAndrew Gallatin goto abort_with_nothing; 2253b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2254b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2255b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2256b2fc195eSAndrew Gallatin 2257aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2258aed8e389SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC; 2259aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2260aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2261aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2262aed8e389SAndrew Gallatin goto abort_with_alloc; 2263aed8e389SAndrew Gallatin 2264b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2265b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2266b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2267b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2268b2fc195eSAndrew Gallatin goto abort_with_alloc; 2269b2fc195eSAndrew Gallatin 2270b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2271b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2272b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2273b2fc195eSAndrew Gallatin goto abort_with_alloc; 2274b2fc195eSAndrew Gallatin 2275b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2276b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2277b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2278b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2279b2fc195eSAndrew Gallatin goto abort_with_alloc; 2280b2fc195eSAndrew Gallatin 2281b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2282b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2283b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2284b2fc195eSAndrew Gallatin goto abort_with_alloc; 2285b2fc195eSAndrew Gallatin 2286b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2287b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2288b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2289b2fc195eSAndrew Gallatin goto abort_with_alloc; 2290b2fc195eSAndrew Gallatin 2291b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2292b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2293b2fc195eSAndrew Gallatin 1, /* alignment */ 2294b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2295b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2296b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2297b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2298aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2299aed8e389SAndrew Gallatin MXGE_MAX_SEND_DESC/2, /* num segs */ 2300b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2301b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2302b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2303b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2304b2fc195eSAndrew Gallatin 2305b2fc195eSAndrew Gallatin if (err != 0) { 2306b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2307b2fc195eSAndrew Gallatin err); 2308b2fc195eSAndrew Gallatin goto abort_with_alloc; 2309b2fc195eSAndrew Gallatin } 2310b2fc195eSAndrew Gallatin 2311b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2312b2fc195eSAndrew Gallatin 1, /* alignment */ 2313b2fc195eSAndrew Gallatin 4096, /* boundary */ 2314b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2315b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2316b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2317b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2318b2fc195eSAndrew Gallatin 1, /* num segs */ 2319b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2320b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2321b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2322b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2323b2fc195eSAndrew Gallatin if (err != 0) { 2324b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2325b2fc195eSAndrew Gallatin err); 2326b2fc195eSAndrew Gallatin goto abort_with_alloc; 2327b2fc195eSAndrew Gallatin } 2328b2fc195eSAndrew Gallatin 2329b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2330b2fc195eSAndrew Gallatin 1, /* alignment */ 2331b2fc195eSAndrew Gallatin 4096, /* boundary */ 2332b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2333b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2334b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2335b2fc195eSAndrew Gallatin 4096, /* maxsize */ 2336b2fc195eSAndrew Gallatin 1, /* num segs */ 2337b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2338b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2339b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2340b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2341b2fc195eSAndrew Gallatin if (err != 0) { 2342b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2343b2fc195eSAndrew Gallatin err); 2344b2fc195eSAndrew Gallatin goto abort_with_alloc; 2345b2fc195eSAndrew Gallatin } 2346b2fc195eSAndrew Gallatin 2347b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2348b2fc195eSAndrew Gallatin in each ring */ 2349b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2350b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2351b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2352b2fc195eSAndrew Gallatin if (err != 0) { 2353b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2354b2fc195eSAndrew Gallatin err); 2355b2fc195eSAndrew Gallatin goto abort_with_alloc; 2356b2fc195eSAndrew Gallatin } 2357b2fc195eSAndrew Gallatin } 2358b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2359b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2360b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2361b2fc195eSAndrew Gallatin if (err != 0) { 2362b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2363b2fc195eSAndrew Gallatin err); 2364b2fc195eSAndrew Gallatin goto abort_with_alloc; 2365b2fc195eSAndrew Gallatin } 2366b2fc195eSAndrew Gallatin } 2367b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2368b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2369b2fc195eSAndrew Gallatin if (err != 0) { 2370b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2371b2fc195eSAndrew Gallatin err); 2372b2fc195eSAndrew Gallatin goto abort_with_alloc; 2373b2fc195eSAndrew Gallatin } 2374b2fc195eSAndrew Gallatin 2375b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2376b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2377b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2378b2fc195eSAndrew Gallatin if (err != 0) { 2379b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2380b2fc195eSAndrew Gallatin err); 2381b2fc195eSAndrew Gallatin goto abort_with_alloc; 2382b2fc195eSAndrew Gallatin } 2383b2fc195eSAndrew Gallatin } 2384b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2385b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2386b2fc195eSAndrew Gallatin if (err != 0) { 2387b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2388b2fc195eSAndrew Gallatin err); 2389b2fc195eSAndrew Gallatin goto abort_with_alloc; 2390b2fc195eSAndrew Gallatin } 2391b2fc195eSAndrew Gallatin return 0; 2392b2fc195eSAndrew Gallatin 2393b2fc195eSAndrew Gallatin abort_with_alloc: 23946d87a65dSAndrew Gallatin mxge_free_rings(sc); 2395b2fc195eSAndrew Gallatin 2396b2fc195eSAndrew Gallatin abort_with_nothing: 2397b2fc195eSAndrew Gallatin return err; 2398b2fc195eSAndrew Gallatin } 2399b2fc195eSAndrew Gallatin 2400b2fc195eSAndrew Gallatin static int 24016d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2402b2fc195eSAndrew Gallatin { 24036d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2404b2fc195eSAndrew Gallatin int i, err; 2405b2fc195eSAndrew Gallatin bus_dmamap_t map; 24060fa7f681SAndrew Gallatin bus_addr_t bus; 2407b2fc195eSAndrew Gallatin 2408b2fc195eSAndrew Gallatin 24097d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 24107d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 24117d542e2dSAndrew Gallatin 24126d87a65dSAndrew Gallatin err = mxge_reset(sc); 2413b2fc195eSAndrew Gallatin if (err != 0) { 2414b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2415b2fc195eSAndrew Gallatin return EIO; 2416b2fc195eSAndrew Gallatin } 2417a98d6cd7SAndrew Gallatin bzero(sc->rx_done.entry, 2418a98d6cd7SAndrew Gallatin mxge_max_intr_slots * sizeof(*sc->rx_done.entry)); 2419b2fc195eSAndrew Gallatin 2420b2fc195eSAndrew Gallatin if (MCLBYTES >= 24215e7d8541SAndrew Gallatin sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD) 2422b2fc195eSAndrew Gallatin sc->big_bytes = MCLBYTES; 2423b2fc195eSAndrew Gallatin else 2424b2fc195eSAndrew Gallatin sc->big_bytes = MJUMPAGESIZE; 2425b2fc195eSAndrew Gallatin 2426b2fc195eSAndrew Gallatin 2427b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2428b2fc195eSAndrew Gallatin 24295e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2430b2fc195eSAndrew Gallatin sc->tx.lanai = 2431b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 24326d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 24335e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2434b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2435b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 24365e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2437b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2438b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2439b2fc195eSAndrew Gallatin 2440b2fc195eSAndrew Gallatin if (err != 0) { 2441b2fc195eSAndrew Gallatin device_printf(sc->dev, 2442b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2443a98d6cd7SAndrew Gallatin return EIO; 2444b2fc195eSAndrew Gallatin } 2445b2fc195eSAndrew Gallatin 2446b2fc195eSAndrew Gallatin if (sc->wc) { 24470fa7f681SAndrew Gallatin sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4; 24480fa7f681SAndrew Gallatin sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL; 24490fa7f681SAndrew Gallatin sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG; 2450b2fc195eSAndrew Gallatin } else { 2451b2fc195eSAndrew Gallatin sc->tx.wc_fifo = 0; 2452b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = 0; 2453b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = 0; 2454b2fc195eSAndrew Gallatin } 2455b2fc195eSAndrew Gallatin 2456b2fc195eSAndrew Gallatin 2457b2fc195eSAndrew Gallatin /* stock receive rings */ 2458b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2459b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 24606d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2461b2fc195eSAndrew Gallatin if (err) { 2462b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2463b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2464b2fc195eSAndrew Gallatin goto abort; 2465b2fc195eSAndrew Gallatin } 2466b2fc195eSAndrew Gallatin } 2467b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2468b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 24696d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2470b2fc195eSAndrew Gallatin if (err) { 2471b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2472b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2473b2fc195eSAndrew Gallatin goto abort; 2474b2fc195eSAndrew Gallatin } 2475b2fc195eSAndrew Gallatin } 2476b2fc195eSAndrew Gallatin 2477b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2478b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2479b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2480b2fc195eSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN; 24815e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2482b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 24835e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2484b2fc195eSAndrew Gallatin &cmd); 2485b2fc195eSAndrew Gallatin cmd.data0 = sc->big_bytes; 24865e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 24870fa7f681SAndrew Gallatin 24880fa7f681SAndrew Gallatin if (err != 0) { 24890fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 24900fa7f681SAndrew Gallatin goto abort; 24910fa7f681SAndrew Gallatin } 24920fa7f681SAndrew Gallatin 2493b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 24946d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 24956d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 24960fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 24970fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 24980fa7f681SAndrew Gallatin 24990fa7f681SAndrew Gallatin if (err != 0) { 25000fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 25010fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 25020fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 25030fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 25040fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 25050fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 25060fa7f681SAndrew Gallatin &cmd); 25070fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 25080fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 25090fa7f681SAndrew Gallatin } else { 25100fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 25110fa7f681SAndrew Gallatin } 2512b2fc195eSAndrew Gallatin 2513b2fc195eSAndrew Gallatin if (err != 0) { 2514b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2515b2fc195eSAndrew Gallatin goto abort; 2516b2fc195eSAndrew Gallatin } 2517b2fc195eSAndrew Gallatin 2518b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 25195e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2520b2fc195eSAndrew Gallatin if (err) { 2521b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2522b2fc195eSAndrew Gallatin goto abort; 2523b2fc195eSAndrew Gallatin } 2524b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2525b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2526b2fc195eSAndrew Gallatin 2527b2fc195eSAndrew Gallatin return 0; 2528b2fc195eSAndrew Gallatin 2529b2fc195eSAndrew Gallatin 2530b2fc195eSAndrew Gallatin abort: 25316d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2532a98d6cd7SAndrew Gallatin 2533b2fc195eSAndrew Gallatin return err; 2534b2fc195eSAndrew Gallatin } 2535b2fc195eSAndrew Gallatin 2536b2fc195eSAndrew Gallatin static int 25376d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2538b2fc195eSAndrew Gallatin { 25396d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2540b2fc195eSAndrew Gallatin int err, old_down_cnt; 2541b2fc195eSAndrew Gallatin 2542b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2543b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2544b2fc195eSAndrew Gallatin mb(); 25455e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2546b2fc195eSAndrew Gallatin if (err) { 2547b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2548b2fc195eSAndrew Gallatin } 2549b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2550b2fc195eSAndrew Gallatin /* wait for down irq */ 2551dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 2552b2fc195eSAndrew Gallatin } 2553b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2554b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2555b2fc195eSAndrew Gallatin } 2556a98d6cd7SAndrew Gallatin 25576d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2558a98d6cd7SAndrew Gallatin 2559b2fc195eSAndrew Gallatin return 0; 2560b2fc195eSAndrew Gallatin } 2561b2fc195eSAndrew Gallatin 2562dce01b9bSAndrew Gallatin static void 2563dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 2564dce01b9bSAndrew Gallatin { 2565dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2566dce01b9bSAndrew Gallatin int reg; 2567dce01b9bSAndrew Gallatin uint16_t cmd, lnk, pectl; 2568dce01b9bSAndrew Gallatin 2569dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2570dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2571dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2572dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2573dce01b9bSAndrew Gallatin 2574dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2575dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2576dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 2577dce01b9bSAndrew Gallatin } 2578dce01b9bSAndrew Gallatin 2579dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 2580dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 2581dce01b9bSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2582dce01b9bSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2583dce01b9bSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2584dce01b9bSAndrew Gallatin } 2585dce01b9bSAndrew Gallatin 2586dce01b9bSAndrew Gallatin static uint32_t 2587dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 2588dce01b9bSAndrew Gallatin { 2589dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2590dce01b9bSAndrew Gallatin uint32_t vs; 2591dce01b9bSAndrew Gallatin 2592dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 2593dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { 2594dce01b9bSAndrew Gallatin device_printf(sc->dev, 2595dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 2596dce01b9bSAndrew Gallatin return (uint32_t)-1; 2597dce01b9bSAndrew Gallatin } 2598dce01b9bSAndrew Gallatin /* enable read32 mode */ 2599dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 2600dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 2601dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 2602dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 2603dce01b9bSAndrew Gallatin } 2604dce01b9bSAndrew Gallatin 2605dce01b9bSAndrew Gallatin static void 2606dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 2607dce01b9bSAndrew Gallatin { 2608dce01b9bSAndrew Gallatin int err; 2609dce01b9bSAndrew Gallatin uint32_t reboot; 2610dce01b9bSAndrew Gallatin uint16_t cmd; 2611dce01b9bSAndrew Gallatin 2612dce01b9bSAndrew Gallatin err = ENXIO; 2613dce01b9bSAndrew Gallatin 2614dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 2615dce01b9bSAndrew Gallatin 2616dce01b9bSAndrew Gallatin /* 2617dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 2618dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 2619dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 2620dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 2621dce01b9bSAndrew Gallatin * again 2622dce01b9bSAndrew Gallatin */ 2623dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2624dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2625dce01b9bSAndrew Gallatin /* 2626dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 2627dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 2628dce01b9bSAndrew Gallatin * back, then give up 2629dce01b9bSAndrew Gallatin */ 2630dce01b9bSAndrew Gallatin DELAY(1000*100); 2631dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2632dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2633dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 2634dce01b9bSAndrew Gallatin goto abort; 2635dce01b9bSAndrew Gallatin } 2636dce01b9bSAndrew Gallatin } 2637dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 2638dce01b9bSAndrew Gallatin /* print the reboot status */ 2639dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 2640dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 2641dce01b9bSAndrew Gallatin reboot); 2642dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 2643dce01b9bSAndrew Gallatin 2644dce01b9bSAndrew Gallatin /* XXXX waiting for pci_cfg_restore() to be exported */ 2645dce01b9bSAndrew Gallatin goto abort; /* just abort for now */ 2646dce01b9bSAndrew Gallatin 2647dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 2648dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2649dce01b9bSAndrew Gallatin } else { 2650dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC did not reboot, ring state:\n"); 2651dce01b9bSAndrew Gallatin device_printf(sc->dev, "tx.req=%d tx.done=%d\n", 2652dce01b9bSAndrew Gallatin sc->tx.req, sc->tx.done); 2653dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 2654dce01b9bSAndrew Gallatin sc->tx.pkt_done, 2655dce01b9bSAndrew Gallatin be32toh(sc->fw_stats->send_done_count)); 2656dce01b9bSAndrew Gallatin } 2657dce01b9bSAndrew Gallatin 2658dce01b9bSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { 2659dce01b9bSAndrew Gallatin mxge_close(sc); 2660dce01b9bSAndrew Gallatin err = mxge_open(sc); 2661dce01b9bSAndrew Gallatin } 2662dce01b9bSAndrew Gallatin 2663dce01b9bSAndrew Gallatin abort: 2664dce01b9bSAndrew Gallatin /* 2665dce01b9bSAndrew Gallatin * stop the watchdog if the nic is dead, to avoid spamming the 2666dce01b9bSAndrew Gallatin * console 2667dce01b9bSAndrew Gallatin */ 2668dce01b9bSAndrew Gallatin if (err != 0) { 2669dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2670dce01b9bSAndrew Gallatin } 2671dce01b9bSAndrew Gallatin } 2672dce01b9bSAndrew Gallatin 2673dce01b9bSAndrew Gallatin static void 2674dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 2675dce01b9bSAndrew Gallatin { 2676dce01b9bSAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 2677dce01b9bSAndrew Gallatin 2678dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 2679dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 2680dce01b9bSAndrew Gallatin if (tx->req != tx->done && 2681dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 2682dce01b9bSAndrew Gallatin tx->done == tx->watchdog_done) 2683dce01b9bSAndrew Gallatin mxge_watchdog_reset(sc); 2684dce01b9bSAndrew Gallatin 2685dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 2686dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 2687dce01b9bSAndrew Gallatin } 2688dce01b9bSAndrew Gallatin 2689dce01b9bSAndrew Gallatin static void 2690dce01b9bSAndrew Gallatin mxge_tick(void *arg) 2691dce01b9bSAndrew Gallatin { 2692dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 2693dce01b9bSAndrew Gallatin 2694dce01b9bSAndrew Gallatin 2695dce01b9bSAndrew Gallatin /* Synchronize with possible callout reset/stop. */ 2696dce01b9bSAndrew Gallatin if (callout_pending(&sc->co_hdl) || 2697dce01b9bSAndrew Gallatin !callout_active(&sc->co_hdl)) { 2698dce01b9bSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2699dce01b9bSAndrew Gallatin return; 2700dce01b9bSAndrew Gallatin } 2701dce01b9bSAndrew Gallatin 2702dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2703dce01b9bSAndrew Gallatin mxge_watchdog(sc); 2704dce01b9bSAndrew Gallatin } 2705b2fc195eSAndrew Gallatin 2706b2fc195eSAndrew Gallatin static int 27076d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2708b2fc195eSAndrew Gallatin { 2709b2fc195eSAndrew Gallatin return EINVAL; 2710b2fc195eSAndrew Gallatin } 2711b2fc195eSAndrew Gallatin 2712b2fc195eSAndrew Gallatin static int 27136d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2714b2fc195eSAndrew Gallatin { 2715b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2716b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2717b2fc195eSAndrew Gallatin int err = 0; 2718b2fc195eSAndrew Gallatin 2719b2fc195eSAndrew Gallatin 2720b2fc195eSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN; 27216d87a65dSAndrew Gallatin if ((real_mtu > MXGE_MAX_ETHER_MTU) || 2722b2fc195eSAndrew Gallatin real_mtu < 60) 2723b2fc195eSAndrew Gallatin return EINVAL; 2724a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2725b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2726b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2727b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2728dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 27296d87a65dSAndrew Gallatin mxge_close(sc); 27306d87a65dSAndrew Gallatin err = mxge_open(sc); 2731b2fc195eSAndrew Gallatin if (err != 0) { 2732b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 27336d87a65dSAndrew Gallatin mxge_close(sc); 27346d87a65dSAndrew Gallatin (void) mxge_open(sc); 2735b2fc195eSAndrew Gallatin } 2736dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2737b2fc195eSAndrew Gallatin } 2738a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2739b2fc195eSAndrew Gallatin return err; 2740b2fc195eSAndrew Gallatin } 2741b2fc195eSAndrew Gallatin 2742b2fc195eSAndrew Gallatin static void 27436d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 2744b2fc195eSAndrew Gallatin { 27456d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2746b2fc195eSAndrew Gallatin 2747b2fc195eSAndrew Gallatin 2748b2fc195eSAndrew Gallatin if (sc == NULL) 2749b2fc195eSAndrew Gallatin return; 2750b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 2751b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 2752b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 2753b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 2754b2fc195eSAndrew Gallatin } 2755b2fc195eSAndrew Gallatin 2756b2fc195eSAndrew Gallatin static int 27576d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2758b2fc195eSAndrew Gallatin { 27596d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2760b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 2761b2fc195eSAndrew Gallatin int err, mask; 2762b2fc195eSAndrew Gallatin 2763b2fc195eSAndrew Gallatin err = 0; 2764b2fc195eSAndrew Gallatin switch (command) { 2765b2fc195eSAndrew Gallatin case SIOCSIFADDR: 2766b2fc195eSAndrew Gallatin case SIOCGIFADDR: 2767b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 2768b2fc195eSAndrew Gallatin break; 2769b2fc195eSAndrew Gallatin 2770b2fc195eSAndrew Gallatin case SIOCSIFMTU: 27716d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 2772b2fc195eSAndrew Gallatin break; 2773b2fc195eSAndrew Gallatin 2774b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 2775a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2776b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 2777dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 27786d87a65dSAndrew Gallatin err = mxge_open(sc); 2779dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, 2780dce01b9bSAndrew Gallatin mxge_tick, sc); 2781dce01b9bSAndrew Gallatin } else { 27820fa7f681SAndrew Gallatin /* take care of promis can allmulti 27830fa7f681SAndrew Gallatin flag chages */ 27840fa7f681SAndrew Gallatin mxge_change_promisc(sc, 27850fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 27860fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 27870fa7f681SAndrew Gallatin } 2788b2fc195eSAndrew Gallatin } else { 2789dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 27906d87a65dSAndrew Gallatin mxge_close(sc); 2791dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2792dce01b9bSAndrew Gallatin } 2793b2fc195eSAndrew Gallatin } 2794a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2795b2fc195eSAndrew Gallatin break; 2796b2fc195eSAndrew Gallatin 2797b2fc195eSAndrew Gallatin case SIOCADDMULTI: 2798b2fc195eSAndrew Gallatin case SIOCDELMULTI: 2799a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 28000fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 2801a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2802b2fc195eSAndrew Gallatin break; 2803b2fc195eSAndrew Gallatin 2804b2fc195eSAndrew Gallatin case SIOCSIFCAP: 2805a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2806b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2807b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 2808b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 2809aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 2810aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 2811aed8e389SAndrew Gallatin | CSUM_TSO); 2812b2fc195eSAndrew Gallatin } else { 2813b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 2814b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 2815b2fc195eSAndrew Gallatin } 2816b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 2817b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 2818b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 28195e7d8541SAndrew Gallatin sc->csum_flag = 0; 2820b2fc195eSAndrew Gallatin } else { 2821b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 28225e7d8541SAndrew Gallatin sc->csum_flag = 1; 2823b2fc195eSAndrew Gallatin } 2824b2fc195eSAndrew Gallatin } 2825aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 2826aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 2827aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 2828aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 2829aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 2830aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 2831aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 2832aed8e389SAndrew Gallatin } else { 2833aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 2834aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 2835aed8e389SAndrew Gallatin err = EINVAL; 2836aed8e389SAndrew Gallatin } 2837aed8e389SAndrew Gallatin } 2838a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2839b2fc195eSAndrew Gallatin break; 2840b2fc195eSAndrew Gallatin 2841b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 2842b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 2843b2fc195eSAndrew Gallatin &sc->media, command); 2844b2fc195eSAndrew Gallatin break; 2845b2fc195eSAndrew Gallatin 2846b2fc195eSAndrew Gallatin default: 2847b2fc195eSAndrew Gallatin err = ENOTTY; 2848b2fc195eSAndrew Gallatin } 2849b2fc195eSAndrew Gallatin return err; 2850b2fc195eSAndrew Gallatin } 2851b2fc195eSAndrew Gallatin 2852b2fc195eSAndrew Gallatin static void 28536d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 2854b2fc195eSAndrew Gallatin { 2855b2fc195eSAndrew Gallatin 28566d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 28576d87a65dSAndrew Gallatin &mxge_flow_control); 28586d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 28596d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 28606d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 28616d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 2862d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 2863d91b1b49SAndrew Gallatin &mxge_force_firmware); 28645e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 28655e7d8541SAndrew Gallatin &mxge_deassert_wait); 28665e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 28675e7d8541SAndrew Gallatin &mxge_verbose); 2868dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 2869b2fc195eSAndrew Gallatin 28705e7d8541SAndrew Gallatin if (bootverbose) 28715e7d8541SAndrew Gallatin mxge_verbose = 1; 28726d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 28736d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 2874dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 2875dce01b9bSAndrew Gallatin mxge_ticks = hz; 28766d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 2877b2fc195eSAndrew Gallatin } 2878b2fc195eSAndrew Gallatin 2879b2fc195eSAndrew Gallatin static int 28806d87a65dSAndrew Gallatin mxge_attach(device_t dev) 2881b2fc195eSAndrew Gallatin { 28826d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2883b2fc195eSAndrew Gallatin struct ifnet *ifp; 2884b2fc195eSAndrew Gallatin size_t bytes; 2885dce01b9bSAndrew Gallatin int count, rid, err; 2886b2fc195eSAndrew Gallatin 2887b2fc195eSAndrew Gallatin sc->dev = dev; 28886d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 2889b2fc195eSAndrew Gallatin 2890b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 2891b2fc195eSAndrew Gallatin 1, /* alignment */ 2892b2fc195eSAndrew Gallatin 4096, /* boundary */ 2893b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2894b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2895b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2896aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 28975e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 2898b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2899b2fc195eSAndrew Gallatin 0, /* flags */ 2900b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2901b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 2902b2fc195eSAndrew Gallatin 2903b2fc195eSAndrew Gallatin if (err != 0) { 2904b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 2905b2fc195eSAndrew Gallatin err); 2906b2fc195eSAndrew Gallatin goto abort_with_nothing; 2907b2fc195eSAndrew Gallatin } 2908b2fc195eSAndrew Gallatin 2909b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 2910b2fc195eSAndrew Gallatin if (ifp == NULL) { 2911b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 2912b2fc195eSAndrew Gallatin err = ENOSPC; 2913b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 2914b2fc195eSAndrew Gallatin } 2915a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 2916a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 2917a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 2918a98d6cd7SAndrew Gallatin snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", 2919a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 2920a98d6cd7SAndrew Gallatin mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); 2921a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 2922a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 2923a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 2924b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2925b2fc195eSAndrew Gallatin 2926dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 2927d91b1b49SAndrew Gallatin 2928dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2929b2fc195eSAndrew Gallatin 2930b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 2931b2fc195eSAndrew Gallatin rid = PCIR_BARS; 2932b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 2933b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 2934b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 2935b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 2936b2fc195eSAndrew Gallatin err = ENXIO; 2937b2fc195eSAndrew Gallatin goto abort_with_lock; 2938b2fc195eSAndrew Gallatin } 2939b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 2940b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 2941b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 2942b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 2943b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 2944b2fc195eSAndrew Gallatin err = ENXIO; 2945b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2946b2fc195eSAndrew Gallatin } 2947b2fc195eSAndrew Gallatin 2948b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 2949b2fc195eSAndrew Gallatin lanai SRAM */ 29506d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 2951b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 2952b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 29536d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 2954b2fc195eSAndrew Gallatin sc->eeprom_strings, 29556d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 29566d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 2957b2fc195eSAndrew Gallatin if (err != 0) 2958b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2959b2fc195eSAndrew Gallatin 2960b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 29616d87a65dSAndrew Gallatin mxge_enable_wc(sc); 2962b2fc195eSAndrew Gallatin 2963b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 29646d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 29656d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 2966b2fc195eSAndrew Gallatin if (err != 0) 2967b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2968b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 29696d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 2970b2fc195eSAndrew Gallatin if (err != 0) 2971b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 2972b2fc195eSAndrew Gallatin 29736d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 2974b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 2975b2fc195eSAndrew Gallatin if (err != 0) 2976b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 29775e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 2978b2fc195eSAndrew Gallatin 2979a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 2980a98d6cd7SAndrew Gallatin if (err != 0) 2981a98d6cd7SAndrew Gallatin goto abort_with_fw_stats; 2982b2fc195eSAndrew Gallatin 2983b2fc195eSAndrew Gallatin /* allocate interrupt queues */ 29845e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry); 29855e7d8541SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2986b2fc195eSAndrew Gallatin if (err != 0) 2987a98d6cd7SAndrew Gallatin goto abort_with_dmabench; 29885e7d8541SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 29895e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2990dc8731d4SAndrew Gallatin 2991b2fc195eSAndrew Gallatin /* Add our ithread */ 2992dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 2993dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 2994dc8731d4SAndrew Gallatin rid = 1; 2995dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 2996dc8731d4SAndrew Gallatin } else { 2997b2fc195eSAndrew Gallatin rid = 0; 2998dc8731d4SAndrew Gallatin } 2999b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 3000b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 3001b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 3002b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 30035e7d8541SAndrew Gallatin goto abort_with_rx_done; 3004b2fc195eSAndrew Gallatin } 3005d91b1b49SAndrew Gallatin if (mxge_verbose) 3006dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 3007dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 3008dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 3009b2fc195eSAndrew Gallatin /* load the firmware */ 30106d87a65dSAndrew Gallatin mxge_select_firmware(sc); 3011b2fc195eSAndrew Gallatin 30126d87a65dSAndrew Gallatin err = mxge_load_firmware(sc); 3013b2fc195eSAndrew Gallatin if (err != 0) 3014b2fc195eSAndrew Gallatin goto abort_with_irq_res; 30155e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 30166d87a65dSAndrew Gallatin err = mxge_reset(sc); 3017b2fc195eSAndrew Gallatin if (err != 0) 3018b2fc195eSAndrew Gallatin goto abort_with_irq_res; 3019b2fc195eSAndrew Gallatin 3020a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 3021a98d6cd7SAndrew Gallatin if (err != 0) { 3022a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 3023a98d6cd7SAndrew Gallatin goto abort_with_irq_res; 3024a98d6cd7SAndrew Gallatin } 3025a98d6cd7SAndrew Gallatin 3026a98d6cd7SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 3027a98d6cd7SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 3028ef544f63SPaolo Pisati NULL, mxge_intr, sc, &sc->ih); 3029a98d6cd7SAndrew Gallatin if (err != 0) { 3030a98d6cd7SAndrew Gallatin goto abort_with_rings; 3031a98d6cd7SAndrew Gallatin } 3032b2fc195eSAndrew Gallatin /* hook into the network stack */ 3033b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 3034b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 3035a82c2581SAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | 3036a82c2581SAndrew Gallatin IFCAP_JUMBO_MTU; 3037aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 3038b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 30395e7d8541SAndrew Gallatin sc->csum_flag = 1; 30406d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 3041b2fc195eSAndrew Gallatin ifp->if_softc = sc; 3042b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 30436d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 30446d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 3045b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 3046b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 30476d87a65dSAndrew Gallatin ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; 3048b2fc195eSAndrew Gallatin 3049b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 30506d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 30516d87a65dSAndrew Gallatin mxge_media_status); 3052b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 30536d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 3054b2fc195eSAndrew Gallatin return 0; 3055b2fc195eSAndrew Gallatin 3056a98d6cd7SAndrew Gallatin abort_with_rings: 3057a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3058b2fc195eSAndrew Gallatin abort_with_irq_res: 3059dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3060dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3061dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3062dc8731d4SAndrew Gallatin pci_release_msi(dev); 30635e7d8541SAndrew Gallatin abort_with_rx_done: 30645e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 30655e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 3066a98d6cd7SAndrew Gallatin abort_with_dmabench: 3067a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 30685e7d8541SAndrew Gallatin abort_with_fw_stats: 30696d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3070b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 30716d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 3072b2fc195eSAndrew Gallatin abort_with_cmd_dma: 30736d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3074b2fc195eSAndrew Gallatin abort_with_mem_res: 3075b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3076b2fc195eSAndrew Gallatin abort_with_lock: 3077b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3078a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3079a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3080a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3081b2fc195eSAndrew Gallatin if_free(ifp); 3082b2fc195eSAndrew Gallatin abort_with_parent_dmat: 3083b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3084b2fc195eSAndrew Gallatin 3085b2fc195eSAndrew Gallatin abort_with_nothing: 3086b2fc195eSAndrew Gallatin return err; 3087b2fc195eSAndrew Gallatin } 3088b2fc195eSAndrew Gallatin 3089b2fc195eSAndrew Gallatin static int 30906d87a65dSAndrew Gallatin mxge_detach(device_t dev) 3091b2fc195eSAndrew Gallatin { 30926d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3093b2fc195eSAndrew Gallatin 3094a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3095b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 30966d87a65dSAndrew Gallatin mxge_close(sc); 3097dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3098a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3099b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 3100dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 3101091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 3102a98d6cd7SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 3103a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3104dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3105dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3106dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3107dc8731d4SAndrew Gallatin pci_release_msi(dev); 3108dc8731d4SAndrew Gallatin 31095e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 31105e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 31116d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3112a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 31136d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 31146d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3115b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3116b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3117a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3118a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3119a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3120b2fc195eSAndrew Gallatin if_free(sc->ifp); 3121b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3122b2fc195eSAndrew Gallatin return 0; 3123b2fc195eSAndrew Gallatin } 3124b2fc195eSAndrew Gallatin 3125b2fc195eSAndrew Gallatin static int 31266d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 3127b2fc195eSAndrew Gallatin { 3128b2fc195eSAndrew Gallatin return 0; 3129b2fc195eSAndrew Gallatin } 3130b2fc195eSAndrew Gallatin 3131b2fc195eSAndrew Gallatin /* 3132b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 3133b2fc195eSAndrew Gallatin 3134b2fc195eSAndrew Gallatin Local Variables: 3135b2fc195eSAndrew Gallatin c-file-style:"linux" 3136b2fc195eSAndrew Gallatin tab-width:8 3137b2fc195eSAndrew Gallatin End: 3138b2fc195eSAndrew Gallatin */ 3139