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; 936d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 946d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 95b2fc195eSAndrew Gallatin 966d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 976d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 986d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 996d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1006d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 101b2fc195eSAndrew Gallatin 1026d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 103b2fc195eSAndrew Gallatin { 104b2fc195eSAndrew Gallatin /* Device interface */ 1056d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1066d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1076d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1086d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 109b2fc195eSAndrew Gallatin {0, 0} 110b2fc195eSAndrew Gallatin }; 111b2fc195eSAndrew Gallatin 1126d87a65dSAndrew Gallatin static driver_t mxge_driver = 113b2fc195eSAndrew Gallatin { 1146d87a65dSAndrew Gallatin "mxge", 1156d87a65dSAndrew Gallatin mxge_methods, 1166d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 117b2fc195eSAndrew Gallatin }; 118b2fc195eSAndrew Gallatin 1196d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 120b2fc195eSAndrew Gallatin 121b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1226d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1236d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 124b2fc195eSAndrew Gallatin 125b2fc195eSAndrew Gallatin static int 1266d87a65dSAndrew Gallatin mxge_probe(device_t dev) 127b2fc195eSAndrew Gallatin { 1286d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1296d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 130b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 131b2fc195eSAndrew Gallatin return 0; 132b2fc195eSAndrew Gallatin } 133b2fc195eSAndrew Gallatin return ENXIO; 134b2fc195eSAndrew Gallatin } 135b2fc195eSAndrew Gallatin 136b2fc195eSAndrew Gallatin static void 1376d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 138b2fc195eSAndrew Gallatin { 139b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 140b2fc195eSAndrew Gallatin vm_paddr_t pa; 141b2fc195eSAndrew Gallatin vm_offset_t len; 142b2fc195eSAndrew Gallatin int err, action; 143b2fc195eSAndrew Gallatin 144b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 145b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 146b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 147b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 148b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 149b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1506d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 151b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 152b2fc195eSAndrew Gallatin if (err != 0) { 153b2fc195eSAndrew Gallatin device_printf(sc->dev, 154b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 155b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 156b2fc195eSAndrew Gallatin } else { 157b2fc195eSAndrew Gallatin sc->wc = 1; 158b2fc195eSAndrew Gallatin } 159b2fc195eSAndrew Gallatin } 160b2fc195eSAndrew Gallatin 161b2fc195eSAndrew Gallatin 162b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 163b2fc195eSAndrew Gallatin static void 1646d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 165b2fc195eSAndrew Gallatin int error) 166b2fc195eSAndrew Gallatin { 167b2fc195eSAndrew Gallatin if (error == 0) { 168b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 169b2fc195eSAndrew Gallatin } 170b2fc195eSAndrew Gallatin } 171b2fc195eSAndrew Gallatin 172b2fc195eSAndrew Gallatin static int 1736d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 174b2fc195eSAndrew Gallatin bus_size_t alignment) 175b2fc195eSAndrew Gallatin { 176b2fc195eSAndrew Gallatin int err; 177b2fc195eSAndrew Gallatin device_t dev = sc->dev; 178b2fc195eSAndrew Gallatin 179b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 180b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 181b2fc195eSAndrew Gallatin alignment, /* alignment */ 182b2fc195eSAndrew Gallatin 4096, /* boundary */ 183b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 184b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 185b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 186b2fc195eSAndrew Gallatin bytes, /* maxsize */ 187b2fc195eSAndrew Gallatin 1, /* num segs */ 188b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 189b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 190b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 191b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 192b2fc195eSAndrew Gallatin if (err != 0) { 193b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 194b2fc195eSAndrew Gallatin return err; 195b2fc195eSAndrew Gallatin } 196b2fc195eSAndrew Gallatin 197b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 198b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 199b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 200b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 201b2fc195eSAndrew Gallatin if (err != 0) { 202b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 203b2fc195eSAndrew Gallatin goto abort_with_dmat; 204b2fc195eSAndrew Gallatin } 205b2fc195eSAndrew Gallatin 206b2fc195eSAndrew Gallatin /* load the memory */ 207b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2086d87a65dSAndrew Gallatin mxge_dmamap_callback, 209b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 210b2fc195eSAndrew Gallatin if (err != 0) { 211b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 212b2fc195eSAndrew Gallatin goto abort_with_mem; 213b2fc195eSAndrew Gallatin } 214b2fc195eSAndrew Gallatin return 0; 215b2fc195eSAndrew Gallatin 216b2fc195eSAndrew Gallatin abort_with_mem: 217b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 218b2fc195eSAndrew Gallatin abort_with_dmat: 219b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 220b2fc195eSAndrew Gallatin return err; 221b2fc195eSAndrew Gallatin } 222b2fc195eSAndrew Gallatin 223b2fc195eSAndrew Gallatin 224b2fc195eSAndrew Gallatin static void 2256d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 226b2fc195eSAndrew Gallatin { 227b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 228b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 229b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 230b2fc195eSAndrew Gallatin } 231b2fc195eSAndrew Gallatin 232b2fc195eSAndrew Gallatin /* 233b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 234b2fc195eSAndrew Gallatin * SN=x\0 235b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 236b2fc195eSAndrew Gallatin * PC=text\0 237b2fc195eSAndrew Gallatin */ 238b2fc195eSAndrew Gallatin 239b2fc195eSAndrew Gallatin static int 2406d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 241b2fc195eSAndrew Gallatin { 2426d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 243b2fc195eSAndrew Gallatin 244b2fc195eSAndrew Gallatin char *ptr, *limit; 245b2fc195eSAndrew Gallatin int i, found_mac; 246b2fc195eSAndrew Gallatin 247b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2486d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 249b2fc195eSAndrew Gallatin found_mac = 0; 250b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 251b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2525e7d8541SAndrew Gallatin ptr += 1; 253b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 254b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2555e7d8541SAndrew Gallatin ptr += 3; 256b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 257b2fc195eSAndrew Gallatin goto abort; 258b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 259b2fc195eSAndrew Gallatin found_mac = 1; 260b2fc195eSAndrew Gallatin } 2615e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2625e7d8541SAndrew Gallatin ptr += 3; 2635e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2645e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2655e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2665e7d8541SAndrew Gallatin ptr += 3; 2675e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2685e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 269b2fc195eSAndrew Gallatin } 2706d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 271b2fc195eSAndrew Gallatin } 272b2fc195eSAndrew Gallatin 273b2fc195eSAndrew Gallatin if (found_mac) 274b2fc195eSAndrew Gallatin return 0; 275b2fc195eSAndrew Gallatin 276b2fc195eSAndrew Gallatin abort: 277b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 278b2fc195eSAndrew Gallatin 279b2fc195eSAndrew Gallatin return ENXIO; 280b2fc195eSAndrew Gallatin } 281b2fc195eSAndrew Gallatin 282b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 283b2fc195eSAndrew Gallatin static int 2846d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 285b2fc195eSAndrew Gallatin { 286b2fc195eSAndrew Gallatin uint32_t val; 287b2fc195eSAndrew Gallatin unsigned long off; 288b2fc195eSAndrew Gallatin char *va, *cfgptr; 289b2fc195eSAndrew Gallatin uint16_t vendor_id, device_id; 290b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 291b2fc195eSAndrew Gallatin uint32_t *ptr32; 292b2fc195eSAndrew Gallatin 293b2fc195eSAndrew Gallatin /* XXXX 294b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 295b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 296b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 297b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 298b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 299b2fc195eSAndrew Gallatin */ 300b2fc195eSAndrew Gallatin #if 0 301b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 302b2fc195eSAndrew Gallatin config space */ 303b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 304b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 305b2fc195eSAndrew Gallatin val |= 0x40; 306b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 307b2fc195eSAndrew Gallatin return 0; 308b2fc195eSAndrew Gallatin } 309b2fc195eSAndrew Gallatin #endif 310b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 311b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 312b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 313b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 314b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 315b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 316b2fc195eSAndrew Gallatin */ 317b2fc195eSAndrew Gallatin 318b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 319b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 320b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 321b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 322b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 323b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 324b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 325b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 326b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 327b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 328b2fc195eSAndrew Gallatin 329b2fc195eSAndrew Gallatin off = 0xe0000000UL 330b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 331b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 332b2fc195eSAndrew Gallatin + 8 * slot); 333b2fc195eSAndrew Gallatin 334b2fc195eSAndrew Gallatin /* map it into the kernel */ 335b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 336b2fc195eSAndrew Gallatin 337b2fc195eSAndrew Gallatin 338b2fc195eSAndrew Gallatin if (va == NULL) { 339b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 340b2fc195eSAndrew Gallatin return EIO; 341b2fc195eSAndrew Gallatin } 342b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 343b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 344b2fc195eSAndrew Gallatin 345b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 346b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 347b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 348b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 349b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 350b2fc195eSAndrew Gallatin vendor_id, device_id); 351b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 352b2fc195eSAndrew Gallatin return EIO; 353b2fc195eSAndrew Gallatin } 354b2fc195eSAndrew Gallatin 355b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 356b2fc195eSAndrew Gallatin val = *ptr32; 357b2fc195eSAndrew Gallatin 358b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 359b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 360b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 361b2fc195eSAndrew Gallatin return EIO; 362b2fc195eSAndrew Gallatin } 363b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 364b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3655e7d8541SAndrew Gallatin if (mxge_verbose) 366b2fc195eSAndrew Gallatin device_printf(sc->dev, 3675e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 3685e7d8541SAndrew Gallatin "at %d:%d:%d\n", 369b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 370b2fc195eSAndrew Gallatin return 0; 371b2fc195eSAndrew Gallatin } 372b2fc195eSAndrew Gallatin #else 373b2fc195eSAndrew Gallatin static int 3746d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 375b2fc195eSAndrew Gallatin { 376b2fc195eSAndrew Gallatin device_printf(sc->dev, 377b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 378b2fc195eSAndrew Gallatin return ENXIO; 379b2fc195eSAndrew Gallatin } 380b2fc195eSAndrew Gallatin #endif 381b2fc195eSAndrew Gallatin /* 382b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 383b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 384b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 385b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 386b2fc195eSAndrew Gallatin * ECRC generation (if supported). 387b2fc195eSAndrew Gallatin * 388b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 389b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 390b2fc195eSAndrew Gallatin * 391b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 392b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 393b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 394b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 395b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 396b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 397b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 398b2fc195eSAndrew Gallatin */ 399b2fc195eSAndrew Gallatin 400b2fc195eSAndrew Gallatin static void 4016d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 402b2fc195eSAndrew Gallatin { 403b2fc195eSAndrew Gallatin int err, aligned = 0; 404b2fc195eSAndrew Gallatin device_t pdev; 405b2fc195eSAndrew Gallatin uint16_t pvend, pdid; 406b2fc195eSAndrew Gallatin 407d91b1b49SAndrew Gallatin 408d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 409d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 410d91b1b49SAndrew Gallatin aligned = 1; 411d91b1b49SAndrew Gallatin else 412d91b1b49SAndrew Gallatin aligned = 0; 413d91b1b49SAndrew Gallatin if (mxge_verbose) 414d91b1b49SAndrew Gallatin device_printf(sc->dev, 415d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 416d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 417d91b1b49SAndrew Gallatin goto abort; 418d91b1b49SAndrew Gallatin } 419d91b1b49SAndrew Gallatin 420d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 421d91b1b49SAndrew Gallatin firmware and skip any checks */ 422d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 423d91b1b49SAndrew Gallatin device_printf(sc->dev, 424d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 425d91b1b49SAndrew Gallatin sc->link_width); 426d91b1b49SAndrew Gallatin aligned = 1; 427d91b1b49SAndrew Gallatin goto abort; 428d91b1b49SAndrew Gallatin } 429d91b1b49SAndrew Gallatin 430b2fc195eSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 431b2fc195eSAndrew Gallatin if (pdev == NULL) { 432b2fc195eSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 433b2fc195eSAndrew Gallatin goto abort; 434b2fc195eSAndrew Gallatin } 435b2fc195eSAndrew Gallatin pvend = pci_read_config(pdev, PCIR_VENDOR, 2); 436b2fc195eSAndrew Gallatin pdid = pci_read_config(pdev, PCIR_DEVICE, 2); 437b2fc195eSAndrew Gallatin 438b2fc195eSAndrew Gallatin /* see if we can enable ECRC's on an upstream 439b2fc195eSAndrew Gallatin Nvidia bridge */ 4406d87a65dSAndrew Gallatin if (mxge_nvidia_ecrc_enable && 441b2fc195eSAndrew Gallatin (pvend == 0x10de && pdid == 0x005d)) { 4426d87a65dSAndrew Gallatin err = mxge_enable_nvidia_ecrc(sc, pdev); 443b2fc195eSAndrew Gallatin if (err == 0) { 444b2fc195eSAndrew Gallatin aligned = 1; 4455e7d8541SAndrew Gallatin if (mxge_verbose) 446b2fc195eSAndrew Gallatin device_printf(sc->dev, 4475e7d8541SAndrew Gallatin "Assuming aligned completions" 4485e7d8541SAndrew Gallatin " (ECRC)\n"); 449b2fc195eSAndrew Gallatin } 450b2fc195eSAndrew Gallatin } 451b2fc195eSAndrew Gallatin /* see if the upstream bridge is known to 452b2fc195eSAndrew Gallatin provided aligned completions */ 453b2fc195eSAndrew Gallatin if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) || 4540fa7f681SAndrew Gallatin /* PLX */ (pvend == 0x10b5 && pdid == 0x8532) || 4550fa7f681SAndrew Gallatin /* Intel */ (pvend == 0x8086 && 456d91b1b49SAndrew Gallatin /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) || 457d91b1b49SAndrew Gallatin /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) { 458d91b1b49SAndrew Gallatin aligned = 1; 4595e7d8541SAndrew Gallatin if (mxge_verbose) 460b2fc195eSAndrew Gallatin device_printf(sc->dev, 4615e7d8541SAndrew Gallatin "Assuming aligned completions " 4625e7d8541SAndrew Gallatin "(0x%x:0x%x)\n", pvend, pdid); 463b2fc195eSAndrew Gallatin } 464b2fc195eSAndrew Gallatin 465b2fc195eSAndrew Gallatin abort: 466b2fc195eSAndrew Gallatin if (aligned) { 4676d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 468b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 469b2fc195eSAndrew Gallatin } else { 4706d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 471b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 472b2fc195eSAndrew Gallatin } 473b2fc195eSAndrew Gallatin } 474b2fc195eSAndrew Gallatin 475b2fc195eSAndrew Gallatin union qualhack 476b2fc195eSAndrew Gallatin { 477b2fc195eSAndrew Gallatin const char *ro_char; 478b2fc195eSAndrew Gallatin char *rw_char; 479b2fc195eSAndrew Gallatin }; 480b2fc195eSAndrew Gallatin 4814da0d523SAndrew Gallatin static int 4824da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 4834da0d523SAndrew Gallatin { 4844da0d523SAndrew Gallatin int major, minor; 4854da0d523SAndrew Gallatin 4864da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 4874da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 4884da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 4894da0d523SAndrew Gallatin return EIO; 4904da0d523SAndrew Gallatin } 4914da0d523SAndrew Gallatin 4924da0d523SAndrew Gallatin /* save firmware version for sysctl */ 4934da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 4944da0d523SAndrew Gallatin if (mxge_verbose) 4954da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 4964da0d523SAndrew Gallatin 4974da0d523SAndrew Gallatin sscanf(sc->fw_version, "%d.%d", &major, &minor); 4984da0d523SAndrew Gallatin 4994da0d523SAndrew Gallatin if (!(major == MXGEFW_VERSION_MAJOR 5004da0d523SAndrew Gallatin && minor == MXGEFW_VERSION_MINOR)) { 5014da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 5024da0d523SAndrew Gallatin sc->fw_version); 5034da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 5044da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 5054da0d523SAndrew Gallatin return EINVAL; 5064da0d523SAndrew Gallatin } 5074da0d523SAndrew Gallatin return 0; 5084da0d523SAndrew Gallatin 5094da0d523SAndrew Gallatin } 510b2fc195eSAndrew Gallatin 511b2fc195eSAndrew Gallatin static int 5126d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 513b2fc195eSAndrew Gallatin { 514b2fc195eSAndrew Gallatin struct firmware *fw; 515b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 516b2fc195eSAndrew Gallatin unsigned hdr_offset; 517b2fc195eSAndrew Gallatin const char *fw_data; 518b2fc195eSAndrew Gallatin union qualhack hack; 519b2fc195eSAndrew Gallatin int status; 5204da0d523SAndrew Gallatin unsigned int i; 5214da0d523SAndrew Gallatin char dummy; 522b2fc195eSAndrew Gallatin 523b2fc195eSAndrew Gallatin 524b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 525b2fc195eSAndrew Gallatin 526b2fc195eSAndrew Gallatin if (fw == NULL) { 527b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 528b2fc195eSAndrew Gallatin sc->fw_name); 529b2fc195eSAndrew Gallatin return ENOENT; 530b2fc195eSAndrew Gallatin } 531b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 532b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 533b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 534b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 535b2fc195eSAndrew Gallatin status = ENOSPC; 536b2fc195eSAndrew Gallatin goto abort_with_fw; 537b2fc195eSAndrew Gallatin } 538b2fc195eSAndrew Gallatin *limit = fw->datasize; 539b2fc195eSAndrew Gallatin 540b2fc195eSAndrew Gallatin /* check id */ 541b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 542b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 543b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 544b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 545b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 546b2fc195eSAndrew Gallatin status = EIO; 547b2fc195eSAndrew Gallatin goto abort_with_fw; 548b2fc195eSAndrew Gallatin } 549b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 550b2fc195eSAndrew Gallatin 5514da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 5524da0d523SAndrew Gallatin if (status != 0) 5534da0d523SAndrew Gallatin goto abort_with_fw; 554b2fc195eSAndrew Gallatin 555b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 556b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 5574da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 5584da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 5594da0d523SAndrew Gallatin hack.rw_char + i, 5604da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 5614da0d523SAndrew Gallatin mb(); 5624da0d523SAndrew Gallatin dummy = *sc->sram; 5634da0d523SAndrew Gallatin mb(); 5644da0d523SAndrew Gallatin } 565b2fc195eSAndrew Gallatin 566b2fc195eSAndrew Gallatin status = 0; 567b2fc195eSAndrew Gallatin abort_with_fw: 568b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 569b2fc195eSAndrew Gallatin return status; 570b2fc195eSAndrew Gallatin } 571b2fc195eSAndrew Gallatin 572b2fc195eSAndrew Gallatin /* 573b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 574b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 575b2fc195eSAndrew Gallatin */ 576b2fc195eSAndrew Gallatin 577b2fc195eSAndrew Gallatin static void 5786d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 579b2fc195eSAndrew Gallatin { 580b2fc195eSAndrew Gallatin char buf_bytes[72]; 581b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 582b2fc195eSAndrew Gallatin volatile char *submit; 583b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 584b2fc195eSAndrew Gallatin int i; 585b2fc195eSAndrew Gallatin 586b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 587b2fc195eSAndrew Gallatin 588b2fc195eSAndrew Gallatin /* clear confirmation addr */ 589b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 590b2fc195eSAndrew Gallatin *confirm = 0; 591b2fc195eSAndrew Gallatin mb(); 592b2fc195eSAndrew Gallatin 593b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 594b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 595b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 596b2fc195eSAndrew Gallatin */ 597b2fc195eSAndrew Gallatin 5986d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 5996d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 600b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 601b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 602b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 6036d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 6046d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 605b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 606b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 607b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 608b2fc195eSAndrew Gallatin 609b2fc195eSAndrew Gallatin 6100fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 611b2fc195eSAndrew Gallatin 6126d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 613b2fc195eSAndrew Gallatin mb(); 614b2fc195eSAndrew Gallatin DELAY(1000); 615b2fc195eSAndrew Gallatin mb(); 616b2fc195eSAndrew Gallatin i = 0; 617b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 618b2fc195eSAndrew Gallatin DELAY(1000); 619b2fc195eSAndrew Gallatin i++; 620b2fc195eSAndrew Gallatin } 621b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 622b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 623b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 624b2fc195eSAndrew Gallatin *confirm); 625b2fc195eSAndrew Gallatin } 626b2fc195eSAndrew Gallatin return; 627b2fc195eSAndrew Gallatin } 628b2fc195eSAndrew Gallatin 629b2fc195eSAndrew Gallatin static int 6306d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 631b2fc195eSAndrew Gallatin { 632b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 633b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 634b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 6350fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 636b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 637b2fc195eSAndrew Gallatin int sleep_total = 0; 638b2fc195eSAndrew Gallatin 639b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 640b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 641b2fc195eSAndrew Gallatin 642b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 643b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 644b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 645b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 6466d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6476d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 648b2fc195eSAndrew Gallatin 649b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 650b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 651b2fc195eSAndrew Gallatin mtx_lock(&sc->cmd_lock); 652b2fc195eSAndrew Gallatin response->result = 0xffffffff; 653b2fc195eSAndrew Gallatin mb(); 6546d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 655b2fc195eSAndrew Gallatin 6565e7d8541SAndrew Gallatin /* wait up to 20ms */ 6575e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 658b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 659b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 660b2fc195eSAndrew Gallatin mb(); 661b2fc195eSAndrew Gallatin if (response->result != 0xffffffff) { 662b2fc195eSAndrew Gallatin if (response->result == 0) { 663b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 664b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 665b2fc195eSAndrew Gallatin return 0; 666b2fc195eSAndrew Gallatin } else { 667b2fc195eSAndrew Gallatin device_printf(sc->dev, 6686d87a65dSAndrew Gallatin "mxge: command %d " 669b2fc195eSAndrew Gallatin "failed, result = %d\n", 670b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 671b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 672b2fc195eSAndrew Gallatin return ENXIO; 673b2fc195eSAndrew Gallatin } 674b2fc195eSAndrew Gallatin } 6755e7d8541SAndrew Gallatin DELAY(1000); 676b2fc195eSAndrew Gallatin } 677b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 6786d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 679b2fc195eSAndrew Gallatin "result = %d\n", 680b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 681b2fc195eSAndrew Gallatin return EAGAIN; 682b2fc195eSAndrew Gallatin } 683b2fc195eSAndrew Gallatin 6844da0d523SAndrew Gallatin static int 6854da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 6864da0d523SAndrew Gallatin { 6874da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 6884da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 6894da0d523SAndrew Gallatin size_t hdr_offset; 6904da0d523SAndrew Gallatin int status; 6914da0d523SAndrew Gallatin 6924da0d523SAndrew Gallatin /* find running firmware header */ 6934da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 6944da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 6954da0d523SAndrew Gallatin 6964da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 6974da0d523SAndrew Gallatin device_printf(sc->dev, 6984da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 6994da0d523SAndrew Gallatin (int)hdr_offset); 7004da0d523SAndrew Gallatin return EIO; 7014da0d523SAndrew Gallatin } 7024da0d523SAndrew Gallatin 7034da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 7044da0d523SAndrew Gallatin * validate firmware */ 7054da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 7064da0d523SAndrew Gallatin if (hdr == NULL) { 7074da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 7084da0d523SAndrew Gallatin return ENOMEM; 7094da0d523SAndrew Gallatin } 7104da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 7114da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 7124da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 7134da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 7144da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 7154da0d523SAndrew Gallatin return status; 7164da0d523SAndrew Gallatin } 7174da0d523SAndrew Gallatin 718b2fc195eSAndrew Gallatin 719b2fc195eSAndrew Gallatin static int 7206d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 721b2fc195eSAndrew Gallatin { 722b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 723b2fc195eSAndrew Gallatin volatile char *submit; 724b2fc195eSAndrew Gallatin char buf_bytes[72]; 725b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 726b2fc195eSAndrew Gallatin int status, i; 727b2fc195eSAndrew Gallatin 728b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 729b2fc195eSAndrew Gallatin 730b2fc195eSAndrew Gallatin size = sc->sram_size; 7316d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 732b2fc195eSAndrew Gallatin if (status) { 7334da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 7344da0d523SAndrew Gallatin it is new enough */ 7354da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 7364da0d523SAndrew Gallatin if (status) { 7374da0d523SAndrew Gallatin device_printf(sc->dev, 7384da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 739b2fc195eSAndrew Gallatin return status; 740b2fc195eSAndrew Gallatin } 7414da0d523SAndrew Gallatin device_printf(sc->dev, 7424da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 7434da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 7444da0d523SAndrew Gallatin device_printf(sc->dev, 7454da0d523SAndrew Gallatin "Using firmware currently running on NIC" 7464da0d523SAndrew Gallatin ". For optimal\n"); 7474da0d523SAndrew Gallatin device_printf(sc->dev, 7484da0d523SAndrew Gallatin "performance consider loading optimized " 7494da0d523SAndrew Gallatin "firmware\n"); 7504da0d523SAndrew Gallatin } 751d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 752d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 753d91b1b49SAndrew Gallatin return 0; 7544da0d523SAndrew Gallatin } 755b2fc195eSAndrew Gallatin /* clear confirmation addr */ 756b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 757b2fc195eSAndrew Gallatin *confirm = 0; 758b2fc195eSAndrew Gallatin mb(); 759b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 760b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 761b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 762b2fc195eSAndrew Gallatin */ 763b2fc195eSAndrew Gallatin 7646d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7656d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 766b2fc195eSAndrew Gallatin 767b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 768b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 769b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 770b2fc195eSAndrew Gallatin 771b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 772b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 773b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 774b2fc195eSAndrew Gallatin */ 775b2fc195eSAndrew Gallatin /* where the code starts*/ 7766d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 777b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 778b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 779b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 780b2fc195eSAndrew Gallatin 7810fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 7826d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 783b2fc195eSAndrew Gallatin mb(); 784b2fc195eSAndrew Gallatin DELAY(1000); 785b2fc195eSAndrew Gallatin mb(); 786b2fc195eSAndrew Gallatin i = 0; 787b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 788b2fc195eSAndrew Gallatin DELAY(1000*10); 789b2fc195eSAndrew Gallatin i++; 790b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 791b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 792b2fc195eSAndrew Gallatin } 793b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 794b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 795b2fc195eSAndrew Gallatin confirm, *confirm); 796b2fc195eSAndrew Gallatin 797b2fc195eSAndrew Gallatin return ENXIO; 798b2fc195eSAndrew Gallatin } 799b2fc195eSAndrew Gallatin return 0; 800b2fc195eSAndrew Gallatin } 801b2fc195eSAndrew Gallatin 802b2fc195eSAndrew Gallatin static int 8036d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 804b2fc195eSAndrew Gallatin { 8056d87a65dSAndrew Gallatin mxge_cmd_t cmd; 806b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 807b2fc195eSAndrew Gallatin int status; 808b2fc195eSAndrew Gallatin 809b2fc195eSAndrew Gallatin 810b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 811b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 812b2fc195eSAndrew Gallatin 813b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 814b2fc195eSAndrew Gallatin 8155e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 816b2fc195eSAndrew Gallatin return status; 817b2fc195eSAndrew Gallatin } 818b2fc195eSAndrew Gallatin 819b2fc195eSAndrew Gallatin static int 8206d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 821b2fc195eSAndrew Gallatin { 8226d87a65dSAndrew Gallatin mxge_cmd_t cmd; 823b2fc195eSAndrew Gallatin int status; 824b2fc195eSAndrew Gallatin 825b2fc195eSAndrew Gallatin if (pause) 8265e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 827b2fc195eSAndrew Gallatin &cmd); 828b2fc195eSAndrew Gallatin else 8295e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 830b2fc195eSAndrew Gallatin &cmd); 831b2fc195eSAndrew Gallatin 832b2fc195eSAndrew Gallatin if (status) { 833b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 834b2fc195eSAndrew Gallatin return ENXIO; 835b2fc195eSAndrew Gallatin } 836b2fc195eSAndrew Gallatin sc->pause = pause; 837b2fc195eSAndrew Gallatin return 0; 838b2fc195eSAndrew Gallatin } 839b2fc195eSAndrew Gallatin 840b2fc195eSAndrew Gallatin static void 8416d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 842b2fc195eSAndrew Gallatin { 8436d87a65dSAndrew Gallatin mxge_cmd_t cmd; 844b2fc195eSAndrew Gallatin int status; 845b2fc195eSAndrew Gallatin 846b2fc195eSAndrew Gallatin if (promisc) 8475e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 848b2fc195eSAndrew Gallatin &cmd); 849b2fc195eSAndrew Gallatin else 8505e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 851b2fc195eSAndrew Gallatin &cmd); 852b2fc195eSAndrew Gallatin 853b2fc195eSAndrew Gallatin if (status) { 854b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 855b2fc195eSAndrew Gallatin } 856b2fc195eSAndrew Gallatin } 857b2fc195eSAndrew Gallatin 8580fa7f681SAndrew Gallatin static void 8590fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 8600fa7f681SAndrew Gallatin { 8610fa7f681SAndrew Gallatin mxge_cmd_t cmd; 8620fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 8630fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 8640fa7f681SAndrew Gallatin int err; 8650fa7f681SAndrew Gallatin 8660fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 8670fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 8680fa7f681SAndrew Gallatin return; 8690fa7f681SAndrew Gallatin 8700fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 8710fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 8720fa7f681SAndrew Gallatin if (err != 0) { 8730fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 8740fa7f681SAndrew Gallatin " error status: %d\n", err); 8750fa7f681SAndrew Gallatin return; 8760fa7f681SAndrew Gallatin } 8770fa7f681SAndrew Gallatin 8780fa7f681SAndrew Gallatin 8790fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 8800fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 8810fa7f681SAndrew Gallatin return; 8820fa7f681SAndrew Gallatin 8830fa7f681SAndrew Gallatin /* Flush all the filters */ 8840fa7f681SAndrew Gallatin 8850fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 8860fa7f681SAndrew Gallatin if (err != 0) { 8870fa7f681SAndrew Gallatin device_printf(sc->dev, 8880fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 8890fa7f681SAndrew Gallatin ", error status: %d\n", err); 8900fa7f681SAndrew Gallatin return; 8910fa7f681SAndrew Gallatin } 8920fa7f681SAndrew Gallatin 8930fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 8940fa7f681SAndrew Gallatin 8950fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 8960fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 8970fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 8980fa7f681SAndrew Gallatin continue; 8990fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 9000fa7f681SAndrew Gallatin &cmd.data0, 4); 9010fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 9020fa7f681SAndrew Gallatin &cmd.data1, 2); 9030fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 9040fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 9050fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 9060fa7f681SAndrew Gallatin if (err != 0) { 9070fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 9080fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 9090fa7f681SAndrew Gallatin "%d\t", err); 9100fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 9110fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9120fa7f681SAndrew Gallatin return; 9130fa7f681SAndrew Gallatin } 9140fa7f681SAndrew Gallatin } 9150fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9160fa7f681SAndrew Gallatin /* Enable multicast filtering */ 9170fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 9180fa7f681SAndrew Gallatin if (err != 0) { 9190fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 9200fa7f681SAndrew Gallatin ", error status: %d\n", err); 9210fa7f681SAndrew Gallatin } 9220fa7f681SAndrew Gallatin } 9230fa7f681SAndrew Gallatin 9240fa7f681SAndrew Gallatin 925b2fc195eSAndrew Gallatin static int 9266d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc) 927b2fc195eSAndrew Gallatin { 928b2fc195eSAndrew Gallatin 9296d87a65dSAndrew Gallatin mxge_cmd_t cmd; 9305e7d8541SAndrew Gallatin mxge_dma_t dmabench_dma; 9315e7d8541SAndrew Gallatin size_t bytes; 9325e7d8541SAndrew Gallatin int status; 933b2fc195eSAndrew Gallatin 934b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 935b2fc195eSAndrew Gallatin is alive */ 936b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 9375e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 938b2fc195eSAndrew Gallatin if (status != 0) { 939b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 940b2fc195eSAndrew Gallatin return ENXIO; 941b2fc195eSAndrew Gallatin } 942b2fc195eSAndrew Gallatin 943091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 944091feecdSAndrew Gallatin 945b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 9465e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\ 9475e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 9485e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 9495e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 9505e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 9515e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 9525e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 953b2fc195eSAndrew Gallatin 9546d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 9555e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 9565e7d8541SAndrew Gallatin 9575e7d8541SAndrew Gallatin 9585e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 9595e7d8541SAndrew Gallatin 9605e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 9615e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 9625e7d8541SAndrew Gallatin 9635e7d8541SAndrew Gallatin 9645e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 9656d87a65dSAndrew Gallatin &cmd); 9665e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 967b2fc195eSAndrew Gallatin if (status != 0) { 968b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 969b2fc195eSAndrew Gallatin return status; 970b2fc195eSAndrew Gallatin } 971b2fc195eSAndrew Gallatin 9725e7d8541SAndrew Gallatin 9735e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 9745e7d8541SAndrew Gallatin 9755e7d8541SAndrew Gallatin 9765e7d8541SAndrew Gallatin /* run a DMA benchmark */ 9775e7d8541SAndrew Gallatin sc->read_dma = sc->write_dma = sc->read_write_dma = 0; 9785e7d8541SAndrew Gallatin status = mxge_dma_alloc(sc, &dmabench_dma, 4096, 4096); 9795e7d8541SAndrew Gallatin if (status) 9805e7d8541SAndrew Gallatin goto dmabench_fail; 9815e7d8541SAndrew Gallatin 9825e7d8541SAndrew Gallatin /* Read DMA */ 9835e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 9845e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 9855e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10000; 9865e7d8541SAndrew Gallatin 9875e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 9885e7d8541SAndrew Gallatin if (status != 0) 9895e7d8541SAndrew Gallatin device_printf(sc->dev, "read dma benchmark failed\n"); 9905e7d8541SAndrew Gallatin else 9915e7d8541SAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 9925e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 9935e7d8541SAndrew Gallatin 9945e7d8541SAndrew Gallatin /* Write DMA */ 9955e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 9965e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 9975e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x1; 9985e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 9995e7d8541SAndrew Gallatin if (status != 0) 10005e7d8541SAndrew Gallatin device_printf(sc->dev, "write dma benchmark failed\n"); 10015e7d8541SAndrew Gallatin else 10025e7d8541SAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 10035e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10045e7d8541SAndrew Gallatin /* Read/Write DMA */ 10055e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 10065e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 10075e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10001; 10085e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10095e7d8541SAndrew Gallatin if (status != 0) 10105e7d8541SAndrew Gallatin device_printf(sc->dev, "read/write dma benchmark failed\n"); 10115e7d8541SAndrew Gallatin else 10125e7d8541SAndrew Gallatin sc->read_write_dma = 10135e7d8541SAndrew Gallatin ((cmd.data0>>16) * sc->tx.boundary * 2 * 2) / 10145e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10155e7d8541SAndrew Gallatin 10165e7d8541SAndrew Gallatin mxge_dma_free(&dmabench_dma); 10175e7d8541SAndrew Gallatin 10185e7d8541SAndrew Gallatin dmabench_fail: 1019b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 10205e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 10215e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 10225e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 1023b2fc195eSAndrew Gallatin sc->tx.req = 0; 1024b2fc195eSAndrew Gallatin sc->tx.done = 0; 10255e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 1026b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1027b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1028b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 10296d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 10306d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 10316d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 10320fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1033b2fc195eSAndrew Gallatin return status; 1034b2fc195eSAndrew Gallatin } 1035b2fc195eSAndrew Gallatin 1036b2fc195eSAndrew Gallatin static int 10376d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1038b2fc195eSAndrew Gallatin { 10396d87a65dSAndrew Gallatin mxge_softc_t *sc; 1040b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1041b2fc195eSAndrew Gallatin int err; 1042b2fc195eSAndrew Gallatin 1043b2fc195eSAndrew Gallatin sc = arg1; 1044b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1045b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1046b2fc195eSAndrew Gallatin if (err != 0) { 1047b2fc195eSAndrew Gallatin return err; 1048b2fc195eSAndrew Gallatin } 1049b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1050b2fc195eSAndrew Gallatin return 0; 1051b2fc195eSAndrew Gallatin 1052b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1053b2fc195eSAndrew Gallatin return EINVAL; 1054b2fc195eSAndrew Gallatin 1055b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 10565e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1057b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 10585e7d8541SAndrew Gallatin 1059b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 1060b2fc195eSAndrew Gallatin return err; 1061b2fc195eSAndrew Gallatin } 1062b2fc195eSAndrew Gallatin 1063b2fc195eSAndrew Gallatin static int 10646d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1065b2fc195eSAndrew Gallatin { 10666d87a65dSAndrew Gallatin mxge_softc_t *sc; 1067b2fc195eSAndrew Gallatin unsigned int enabled; 1068b2fc195eSAndrew Gallatin int err; 1069b2fc195eSAndrew Gallatin 1070b2fc195eSAndrew Gallatin sc = arg1; 1071b2fc195eSAndrew Gallatin enabled = sc->pause; 1072b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1073b2fc195eSAndrew Gallatin if (err != 0) { 1074b2fc195eSAndrew Gallatin return err; 1075b2fc195eSAndrew Gallatin } 1076b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1077b2fc195eSAndrew Gallatin return 0; 1078b2fc195eSAndrew Gallatin 1079b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 10806d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1081b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 1082b2fc195eSAndrew Gallatin return err; 1083b2fc195eSAndrew Gallatin } 1084b2fc195eSAndrew Gallatin 1085b2fc195eSAndrew Gallatin static int 10866d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1087b2fc195eSAndrew Gallatin { 1088b2fc195eSAndrew Gallatin int err; 1089b2fc195eSAndrew Gallatin 1090b2fc195eSAndrew Gallatin if (arg1 == NULL) 1091b2fc195eSAndrew Gallatin return EFAULT; 1092b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1093b2fc195eSAndrew Gallatin arg1 = NULL; 1094b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1095b2fc195eSAndrew Gallatin 1096b2fc195eSAndrew Gallatin return err; 1097b2fc195eSAndrew Gallatin } 1098b2fc195eSAndrew Gallatin 1099b2fc195eSAndrew Gallatin static void 11006d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1101b2fc195eSAndrew Gallatin { 1102b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1103b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 11045e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1105b2fc195eSAndrew Gallatin 1106b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1107b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1108b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1109b2fc195eSAndrew Gallatin 11105e7d8541SAndrew Gallatin /* random information */ 11115e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11125e7d8541SAndrew Gallatin "firmware_version", 11135e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 11145e7d8541SAndrew Gallatin 0, "firmware version"); 11155e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11165e7d8541SAndrew Gallatin "serial_number", 11175e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 11185e7d8541SAndrew Gallatin 0, "serial number"); 11195e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11205e7d8541SAndrew Gallatin "product_code", 11215e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 11225e7d8541SAndrew Gallatin 0, "product_code"); 11235e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1124d91b1b49SAndrew Gallatin "pcie_link_width", 1125d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1126d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1127d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11285e7d8541SAndrew Gallatin "tx_boundary", 11295e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 11305e7d8541SAndrew Gallatin 0, "tx_boundary"); 11315e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1132091feecdSAndrew Gallatin "write_combine", 1133091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1134091feecdSAndrew Gallatin 0, "write combining PIO?"); 1135091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11365e7d8541SAndrew Gallatin "read_dma_MBs", 11375e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 11385e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 11395e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11405e7d8541SAndrew Gallatin "write_dma_MBs", 11415e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 11425e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 11435e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11445e7d8541SAndrew Gallatin "read_write_dma_MBs", 11455e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 11465e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 11475e7d8541SAndrew Gallatin 11485e7d8541SAndrew Gallatin 11495e7d8541SAndrew Gallatin /* performance related tunables */ 1150b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1151b2fc195eSAndrew Gallatin "intr_coal_delay", 1152b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11536d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1154b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1155b2fc195eSAndrew Gallatin 1156b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1157b2fc195eSAndrew Gallatin "flow_control_enabled", 1158b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11596d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1160b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1161b2fc195eSAndrew Gallatin 1162b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11635e7d8541SAndrew Gallatin "deassert_wait", 11645e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 11655e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1166b2fc195eSAndrew Gallatin 1167b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1168b2fc195eSAndrew Gallatin Need to swap it */ 1169b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1170b2fc195eSAndrew Gallatin "link_up", 1171b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 11726d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1173b2fc195eSAndrew Gallatin "I", "link up"); 1174b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1175b2fc195eSAndrew Gallatin "rdma_tags_available", 1176b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 11776d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1178b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1179b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1180b2fc195eSAndrew Gallatin "dropped_link_overflow", 1181b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 11826d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1183b2fc195eSAndrew Gallatin "I", "dropped_link_overflow"); 1184b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1185b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1186b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1187b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 11886d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1189b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1190b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 11910fa7f681SAndrew Gallatin "dropped_multicast_filtered", 11920fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 11930fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 11940fa7f681SAndrew Gallatin 0, mxge_handle_be32, 11950fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 11960fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1197b2fc195eSAndrew Gallatin "dropped_runt", 1198b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 11996d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1200b2fc195eSAndrew Gallatin "I", "dropped_runt"); 1201b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1202b2fc195eSAndrew Gallatin "dropped_overrun", 1203b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 12046d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1205b2fc195eSAndrew Gallatin "I", "dropped_overrun"); 1206b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1207b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1208b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1209b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 12106d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1211b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1212b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1213b2fc195eSAndrew Gallatin "dropped_no_big_buffer", 1214b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 12156d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1216b2fc195eSAndrew Gallatin "I", "dropped_no_big_buffer"); 1217b2fc195eSAndrew Gallatin 1218b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1219b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12205e7d8541SAndrew Gallatin "rx_small_cnt", 12215e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 12225e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12235e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12245e7d8541SAndrew Gallatin "rx_big_cnt", 12255e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 12265e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12275e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1228b2fc195eSAndrew Gallatin "tx_req", 1229b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1230b2fc195eSAndrew Gallatin 0, "tx_req"); 1231b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1232b2fc195eSAndrew Gallatin "tx_done", 1233b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1234b2fc195eSAndrew Gallatin 0, "tx_done"); 1235b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12365e7d8541SAndrew Gallatin "tx_pkt_done", 12375e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 12385e7d8541SAndrew Gallatin 0, "tx_done"); 12395e7d8541SAndrew Gallatin 12405e7d8541SAndrew Gallatin /* verbose printing? */ 1241b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12425e7d8541SAndrew Gallatin "verbose", 12435e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 12445e7d8541SAndrew Gallatin 0, "verbose printing"); 1245b2fc195eSAndrew Gallatin 1246b2fc195eSAndrew Gallatin } 1247b2fc195eSAndrew Gallatin 1248b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1249b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1250b2fc195eSAndrew Gallatin 1251b2fc195eSAndrew Gallatin static inline void 12526d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1253b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1254b2fc195eSAndrew Gallatin { 1255b2fc195eSAndrew Gallatin int idx, starting_slot; 1256b2fc195eSAndrew Gallatin starting_slot = tx->req; 1257b2fc195eSAndrew Gallatin while (cnt > 1) { 1258b2fc195eSAndrew Gallatin cnt--; 1259b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 12606d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1261b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1262b2fc195eSAndrew Gallatin mb(); 1263b2fc195eSAndrew Gallatin } 1264b2fc195eSAndrew Gallatin } 1265b2fc195eSAndrew Gallatin 1266b2fc195eSAndrew Gallatin /* 1267b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1268b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1269b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1270b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1271b2fc195eSAndrew Gallatin */ 1272b2fc195eSAndrew Gallatin 1273b2fc195eSAndrew Gallatin static inline void 12746d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1275b2fc195eSAndrew Gallatin int cnt) 1276b2fc195eSAndrew Gallatin { 1277b2fc195eSAndrew Gallatin int idx, i; 1278b2fc195eSAndrew Gallatin uint32_t *src_ints; 1279b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1280b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1281b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 12825e7d8541SAndrew Gallatin uint8_t last_flags; 1283b2fc195eSAndrew Gallatin 1284b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1285b2fc195eSAndrew Gallatin 12865e7d8541SAndrew Gallatin last_flags = src->flags; 12875e7d8541SAndrew Gallatin src->flags = 0; 1288b2fc195eSAndrew Gallatin mb(); 1289b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1290b2fc195eSAndrew Gallatin srcp = src; 1291b2fc195eSAndrew Gallatin 1292b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1293b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 12946d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1295b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1296b2fc195eSAndrew Gallatin srcp += 2; 1297b2fc195eSAndrew Gallatin dstp += 2; 1298b2fc195eSAndrew Gallatin } 1299b2fc195eSAndrew Gallatin } else { 1300b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1301b2fc195eSAndrew Gallatin that it is submitted below */ 13026d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1303b2fc195eSAndrew Gallatin i = 0; 1304b2fc195eSAndrew Gallatin } 1305b2fc195eSAndrew Gallatin if (i < cnt) { 1306b2fc195eSAndrew Gallatin /* submit the first request */ 13076d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1308b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1309b2fc195eSAndrew Gallatin } 1310b2fc195eSAndrew Gallatin 1311b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 13125e7d8541SAndrew Gallatin src->flags = last_flags; 1313b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1314b2fc195eSAndrew Gallatin src_ints+=3; 1315b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1316b2fc195eSAndrew Gallatin dst_ints+=3; 1317b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1318b2fc195eSAndrew Gallatin tx->req += cnt; 1319b2fc195eSAndrew Gallatin mb(); 1320b2fc195eSAndrew Gallatin } 1321b2fc195eSAndrew Gallatin 1322b2fc195eSAndrew Gallatin static inline void 13236d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt) 1324b2fc195eSAndrew Gallatin { 1325b2fc195eSAndrew Gallatin tx->req += cnt; 1326b2fc195eSAndrew Gallatin mb(); 1327b2fc195eSAndrew Gallatin while (cnt >= 4) { 13286d87a65dSAndrew Gallatin mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64); 1329b2fc195eSAndrew Gallatin mb(); 1330b2fc195eSAndrew Gallatin src += 4; 1331b2fc195eSAndrew Gallatin cnt -= 4; 1332b2fc195eSAndrew Gallatin } 1333b2fc195eSAndrew Gallatin if (cnt > 0) { 1334b2fc195eSAndrew Gallatin /* pad it to 64 bytes. The src is 64 bytes bigger than it 1335b2fc195eSAndrew Gallatin needs to be so that we don't overrun it */ 13360fa7f681SAndrew Gallatin mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64); 1337b2fc195eSAndrew Gallatin mb(); 1338b2fc195eSAndrew Gallatin } 1339b2fc195eSAndrew Gallatin } 1340b2fc195eSAndrew Gallatin 1341b2fc195eSAndrew Gallatin static void 1342aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt) 1343aed8e389SAndrew Gallatin { 1344aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1345aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1346aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1347aed8e389SAndrew Gallatin struct ether_header *eh; 1348aed8e389SAndrew Gallatin struct ip *ip; 1349aed8e389SAndrew Gallatin struct tcphdr *tcp; 1350aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1351aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1352aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1353aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1354aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1355aed8e389SAndrew Gallatin static int once; 1356aed8e389SAndrew Gallatin 1357aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1358aed8e389SAndrew Gallatin 1359aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1360aed8e389SAndrew Gallatin * send loop that we are still in the 1361aed8e389SAndrew Gallatin * header portion of the TSO packet. 1362aed8e389SAndrew Gallatin */ 1363aed8e389SAndrew Gallatin 1364aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1365aed8e389SAndrew Gallatin header together in the first mbuf, copy 1366aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1367aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1368aed8e389SAndrew Gallatin + sizeof (*ip))) { 1369aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1370aed8e389SAndrew Gallatin sc->scratch); 1371aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1372aed8e389SAndrew Gallatin } else { 1373aed8e389SAndrew Gallatin eh = mtod(m, struct ether_header *); 1374aed8e389SAndrew Gallatin } 1375aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1376aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2) 1377aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1378aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2) 1379aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1380aed8e389SAndrew Gallatin eh = (struct ether_header *) sc->scratch; 1381aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1382aed8e389SAndrew Gallatin } 1383aed8e389SAndrew Gallatin 1384aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1385aed8e389SAndrew Gallatin cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2)); 1386aed8e389SAndrew Gallatin 1387aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1388aed8e389SAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1389aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1390aed8e389SAndrew Gallatin 1391aed8e389SAndrew Gallatin 1392aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1393aed8e389SAndrew Gallatin * The firmware figures out where to put 1394aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1395aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1396aed8e389SAndrew Gallatin 1397aed8e389SAndrew Gallatin tx = &sc->tx; 1398aed8e389SAndrew Gallatin req = tx->req_list; 1399aed8e389SAndrew Gallatin seg = tx->seg_list; 1400aed8e389SAndrew Gallatin cnt = 0; 1401aed8e389SAndrew Gallatin rdma_count = 0; 1402aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1403aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1404aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1405aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1406aed8e389SAndrew Gallatin * to 0 after a segment cut. 1407aed8e389SAndrew Gallatin * 1408aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1409aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1410aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1411aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1412aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1413aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1414aed8e389SAndrew Gallatin * 1415aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1416aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1417aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1418aed8e389SAndrew Gallatin */ 1419aed8e389SAndrew Gallatin 1420aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1421aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1422aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1423aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1424aed8e389SAndrew Gallatin len = seglen = seg->ds_len; 1425aed8e389SAndrew Gallatin 1426aed8e389SAndrew Gallatin while (len) { 1427aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1428aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1429aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1430aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1431aed8e389SAndrew Gallatin /* payload */ 1432aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1433aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1434aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1435aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1436aed8e389SAndrew Gallatin flags_next |= next_is_first * 1437aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1438aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1439aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1440aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1441aed8e389SAndrew Gallatin /* header ends */ 1442aed8e389SAndrew Gallatin rdma_count = -1; 1443aed8e389SAndrew Gallatin cum_len_next = 0; 1444aed8e389SAndrew Gallatin seglen = -cum_len; 1445aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1446aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1447aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1448aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1449aed8e389SAndrew Gallatin } 1450aed8e389SAndrew Gallatin 1451aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1452aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1453aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1454aed8e389SAndrew Gallatin req->pad = 0; 1455aed8e389SAndrew Gallatin req->rdma_count = 1; 1456aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1457aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1458aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1459aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1460aed8e389SAndrew Gallatin low += seglen; 1461aed8e389SAndrew Gallatin len -= seglen; 1462aed8e389SAndrew Gallatin cum_len = cum_len_next; 1463aed8e389SAndrew Gallatin flags = flags_next; 1464aed8e389SAndrew Gallatin req++; 1465aed8e389SAndrew Gallatin cnt++; 1466aed8e389SAndrew Gallatin rdma_count++; 1467aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1468aed8e389SAndrew Gallatin cksum_offset -= seglen; 1469aed8e389SAndrew Gallatin else 1470aed8e389SAndrew Gallatin cksum_offset = 0; 1471aed8e389SAndrew Gallatin if (__predict_false(cnt > MXGE_MAX_SEND_DESC)) 1472aed8e389SAndrew Gallatin goto drop; 1473aed8e389SAndrew Gallatin } 1474aed8e389SAndrew Gallatin busdma_seg_cnt--; 1475aed8e389SAndrew Gallatin seg++; 1476aed8e389SAndrew Gallatin } 1477aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1478aed8e389SAndrew Gallatin 1479aed8e389SAndrew Gallatin do { 1480aed8e389SAndrew Gallatin req--; 1481aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1482aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1483aed8e389SAndrew Gallatin 1484aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1485aed8e389SAndrew Gallatin if (tx->wc_fifo == NULL) 1486aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1487aed8e389SAndrew Gallatin else 1488aed8e389SAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1489aed8e389SAndrew Gallatin return; 1490aed8e389SAndrew Gallatin 1491aed8e389SAndrew Gallatin drop: 1492aed8e389SAndrew Gallatin m_freem(m); 1493aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1494aed8e389SAndrew Gallatin if (!once) { 1495aed8e389SAndrew Gallatin printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n"); 1496aed8e389SAndrew Gallatin printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list); 1497aed8e389SAndrew Gallatin once = 1; 1498aed8e389SAndrew Gallatin } 1499aed8e389SAndrew Gallatin return; 1500aed8e389SAndrew Gallatin 1501aed8e389SAndrew Gallatin } 1502aed8e389SAndrew Gallatin 1503aed8e389SAndrew Gallatin static void 15046d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1505b2fc195eSAndrew Gallatin { 1506b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1507b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1508b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1509b2fc195eSAndrew Gallatin struct ifnet *ifp; 15106d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1511b2fc195eSAndrew Gallatin struct ether_header *eh; 1512b2fc195eSAndrew Gallatin struct ip *ip; 1513aed8e389SAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag; 1514aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1515aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1516b2fc195eSAndrew Gallatin 1517b2fc195eSAndrew Gallatin 1518b2fc195eSAndrew Gallatin 1519b2fc195eSAndrew Gallatin ifp = sc->ifp; 1520b2fc195eSAndrew Gallatin tx = &sc->tx; 1521b2fc195eSAndrew Gallatin 1522b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1523b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1524b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1525aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1526b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1527b2fc195eSAndrew Gallatin if (err == EFBIG) { 1528b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1529b2fc195eSAndrew Gallatin to defrag */ 1530b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1531b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1532b2fc195eSAndrew Gallatin goto drop; 1533b2fc195eSAndrew Gallatin } 1534b2fc195eSAndrew Gallatin m = m_tmp; 1535b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1536b2fc195eSAndrew Gallatin tx->info[idx].map, 1537aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1538b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1539b2fc195eSAndrew Gallatin } 1540b2fc195eSAndrew Gallatin if (err != 0) { 1541aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1542aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1543b2fc195eSAndrew Gallatin goto drop; 1544b2fc195eSAndrew Gallatin } 1545b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1546b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 15475e7d8541SAndrew Gallatin tx->info[idx].m = m; 1548b2fc195eSAndrew Gallatin 1549aed8e389SAndrew Gallatin 1550aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1551aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1552aed8e389SAndrew Gallatin mxge_encap_tso(sc, m, cnt); 1553aed8e389SAndrew Gallatin return; 1554aed8e389SAndrew Gallatin } 1555aed8e389SAndrew Gallatin 1556b2fc195eSAndrew Gallatin req = tx->req_list; 1557b2fc195eSAndrew Gallatin cksum_offset = 0; 15585e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 15595e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1560b2fc195eSAndrew Gallatin 1561b2fc195eSAndrew Gallatin /* checksum offloading? */ 1562b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1563aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1564aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1565aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1566aed8e389SAndrew Gallatin + sizeof (*ip))) { 1567aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1568aed8e389SAndrew Gallatin sc->scratch); 1569aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1570aed8e389SAndrew Gallatin } else { 1571b2fc195eSAndrew Gallatin eh = mtod(m, struct ether_header *); 1572aed8e389SAndrew Gallatin } 1573b2fc195eSAndrew Gallatin ip = (struct ip *) (eh + 1); 1574b2fc195eSAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1575b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 15765e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1577b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 15785e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1579aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1580aed8e389SAndrew Gallatin } else { 1581aed8e389SAndrew Gallatin odd_flag = 0; 1582b2fc195eSAndrew Gallatin } 15835e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 15845e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1585b2fc195eSAndrew Gallatin 1586b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1587b2fc195eSAndrew Gallatin cum_len = 0; 1588aed8e389SAndrew Gallatin seg = tx->seg_list; 15895e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1590b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1591b2fc195eSAndrew Gallatin req->addr_low = 15926d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1593b2fc195eSAndrew Gallatin req->addr_high = 15946d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1595b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1596b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1597b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1598b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1599b2fc195eSAndrew Gallatin else 1600b2fc195eSAndrew Gallatin cksum_offset = 0; 16015e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16025e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16035e7d8541SAndrew Gallatin req->rdma_count = 1; 1604aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1605b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1606b2fc195eSAndrew Gallatin seg++; 1607b2fc195eSAndrew Gallatin req++; 1608b2fc195eSAndrew Gallatin req->flags = 0; 1609b2fc195eSAndrew Gallatin } 1610b2fc195eSAndrew Gallatin req--; 1611b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1612b2fc195eSAndrew Gallatin if (cum_len < 60) { 1613b2fc195eSAndrew Gallatin req++; 1614b2fc195eSAndrew Gallatin req->addr_low = 16156d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1616b2fc195eSAndrew Gallatin req->addr_high = 16176d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1618b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 16195e7d8541SAndrew Gallatin req->cksum_offset = 0; 16205e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16215e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16225e7d8541SAndrew Gallatin req->rdma_count = 1; 1623aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1624b2fc195eSAndrew Gallatin cnt++; 1625b2fc195eSAndrew Gallatin } 16265e7d8541SAndrew Gallatin 16275e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 16285e7d8541SAndrew Gallatin #if 0 16295e7d8541SAndrew Gallatin /* print what the firmware will see */ 16305e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 16315e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 16325e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 16335e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 16345e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 16355e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 16365e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 16375e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 16385e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 16395e7d8541SAndrew Gallatin } 16405e7d8541SAndrew Gallatin printf("--------------\n"); 16415e7d8541SAndrew Gallatin #endif 16425e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1643b2fc195eSAndrew Gallatin if (tx->wc_fifo == NULL) 16446d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1645b2fc195eSAndrew Gallatin else 16466d87a65dSAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1647b2fc195eSAndrew Gallatin return; 1648b2fc195eSAndrew Gallatin 1649b2fc195eSAndrew Gallatin drop: 1650b2fc195eSAndrew Gallatin m_freem(m); 1651b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1652b2fc195eSAndrew Gallatin return; 1653b2fc195eSAndrew Gallatin } 1654b2fc195eSAndrew Gallatin 1655b2fc195eSAndrew Gallatin 16566d914a32SAndrew Gallatin 16576d914a32SAndrew Gallatin 16586d914a32SAndrew Gallatin static inline void 16596d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1660b2fc195eSAndrew Gallatin { 1661b2fc195eSAndrew Gallatin struct mbuf *m; 1662b2fc195eSAndrew Gallatin struct ifnet *ifp; 1663b2fc195eSAndrew Gallatin 1664b2fc195eSAndrew Gallatin ifp = sc->ifp; 16656d914a32SAndrew Gallatin while ((sc->tx.mask - (sc->tx.req - sc->tx.done)) 16666d914a32SAndrew Gallatin > MXGE_MAX_SEND_DESC) { 1667b2fc195eSAndrew Gallatin 16686d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 16696d914a32SAndrew Gallatin if (m == NULL) { 16706d914a32SAndrew Gallatin return; 16716d914a32SAndrew Gallatin } 1672b2fc195eSAndrew Gallatin /* let BPF see it */ 1673b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1674b2fc195eSAndrew Gallatin 1675b2fc195eSAndrew Gallatin /* give it to the nic */ 16766d87a65dSAndrew Gallatin mxge_encap(sc, m); 16776d914a32SAndrew Gallatin } 16786d914a32SAndrew Gallatin /* ran out of transmit slots */ 1679b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1680b2fc195eSAndrew Gallatin } 1681b2fc195eSAndrew Gallatin 1682b2fc195eSAndrew Gallatin static void 16836d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1684b2fc195eSAndrew Gallatin { 16856d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1686b2fc195eSAndrew Gallatin 1687b2fc195eSAndrew Gallatin 1688b2fc195eSAndrew Gallatin mtx_lock(&sc->tx_lock); 16896d87a65dSAndrew Gallatin mxge_start_locked(sc); 1690b2fc195eSAndrew Gallatin mtx_unlock(&sc->tx_lock); 1691b2fc195eSAndrew Gallatin } 1692b2fc195eSAndrew Gallatin 16935e7d8541SAndrew Gallatin /* 16945e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 16955e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 16965e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 16975e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 16985e7d8541SAndrew Gallatin * in a burst 16995e7d8541SAndrew Gallatin */ 17005e7d8541SAndrew Gallatin static inline void 17015e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 17025e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 17035e7d8541SAndrew Gallatin { 17045e7d8541SAndrew Gallatin uint32_t low; 17055e7d8541SAndrew Gallatin 17065e7d8541SAndrew Gallatin low = src->addr_low; 17075e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 17085e7d8541SAndrew Gallatin mxge_pio_copy(dst, src, 8 * sizeof (*src)); 17095e7d8541SAndrew Gallatin mb(); 17105e7d8541SAndrew Gallatin dst->addr_low = low; 17115e7d8541SAndrew Gallatin mb(); 17125e7d8541SAndrew Gallatin } 17135e7d8541SAndrew Gallatin 1714b2fc195eSAndrew Gallatin static int 17156d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1716b2fc195eSAndrew Gallatin { 1717b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1718b2fc195eSAndrew Gallatin struct mbuf *m; 17196d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1720b2fc195eSAndrew Gallatin int cnt, err; 1721b2fc195eSAndrew Gallatin 1722b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1723b2fc195eSAndrew Gallatin if (m == NULL) { 1724b2fc195eSAndrew Gallatin rx->alloc_fail++; 1725b2fc195eSAndrew Gallatin err = ENOBUFS; 1726b2fc195eSAndrew Gallatin goto done; 1727b2fc195eSAndrew Gallatin } 1728b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1729b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1730b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1731b2fc195eSAndrew Gallatin if (err != 0) { 1732b2fc195eSAndrew Gallatin m_free(m); 1733b2fc195eSAndrew Gallatin goto done; 1734b2fc195eSAndrew Gallatin } 1735b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1736b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 17376d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1738b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 17396d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1740b2fc195eSAndrew Gallatin 1741b2fc195eSAndrew Gallatin done: 1742b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 17435e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 17445e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 17455e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 17465e7d8541SAndrew Gallatin else { 1747b2fc195eSAndrew Gallatin mb(); 17485e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 17495e7d8541SAndrew Gallatin } 1750b2fc195eSAndrew Gallatin } 1751b2fc195eSAndrew Gallatin return err; 1752b2fc195eSAndrew Gallatin } 1753b2fc195eSAndrew Gallatin 1754b2fc195eSAndrew Gallatin static int 17556d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1756b2fc195eSAndrew Gallatin { 1757b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1758b2fc195eSAndrew Gallatin struct mbuf *m; 17596d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1760b2fc195eSAndrew Gallatin int cnt, err; 1761b2fc195eSAndrew Gallatin 1762b2fc195eSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes); 1763b2fc195eSAndrew Gallatin if (m == NULL) { 1764b2fc195eSAndrew Gallatin rx->alloc_fail++; 1765b2fc195eSAndrew Gallatin err = ENOBUFS; 1766b2fc195eSAndrew Gallatin goto done; 1767b2fc195eSAndrew Gallatin } 1768b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1769b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1770b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1771b2fc195eSAndrew Gallatin if (err != 0) { 1772b2fc195eSAndrew Gallatin m_free(m); 1773b2fc195eSAndrew Gallatin goto done; 1774b2fc195eSAndrew Gallatin } 1775b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1776b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 17776d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1778b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 17796d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1780b2fc195eSAndrew Gallatin 1781b2fc195eSAndrew Gallatin done: 1782b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 17835e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 17845e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 17855e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 17865e7d8541SAndrew Gallatin else { 1787b2fc195eSAndrew Gallatin mb(); 17885e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 17895e7d8541SAndrew Gallatin } 1790b2fc195eSAndrew Gallatin } 1791b2fc195eSAndrew Gallatin return err; 1792b2fc195eSAndrew Gallatin } 1793b2fc195eSAndrew Gallatin 1794b2fc195eSAndrew Gallatin static inline void 17955e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 17965e7d8541SAndrew Gallatin { 17975e7d8541SAndrew Gallatin struct ether_header *eh; 17985e7d8541SAndrew Gallatin struct ip *ip; 17995e7d8541SAndrew Gallatin 18005e7d8541SAndrew Gallatin eh = mtod(m, struct ether_header *); 18015e7d8541SAndrew Gallatin if (__predict_true(eh->ether_type == htons(ETHERTYPE_IP))) { 18025e7d8541SAndrew Gallatin ip = (struct ip *)(eh + 1); 18035e7d8541SAndrew Gallatin if (__predict_true(ip->ip_p == IPPROTO_TCP || 18045e7d8541SAndrew Gallatin ip->ip_p == IPPROTO_UDP)) { 18055e7d8541SAndrew Gallatin m->m_pkthdr.csum_data = csum; 18065e7d8541SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_DATA_VALID; 18075e7d8541SAndrew Gallatin } 18085e7d8541SAndrew Gallatin } 18095e7d8541SAndrew Gallatin } 18105e7d8541SAndrew Gallatin 18115e7d8541SAndrew Gallatin static inline void 18125e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum) 1813b2fc195eSAndrew Gallatin { 1814b2fc195eSAndrew Gallatin struct ifnet *ifp; 1815b2fc195eSAndrew Gallatin struct mbuf *m = 0; /* -Wunitialized */ 1816b2fc195eSAndrew Gallatin struct mbuf *m_prev = 0; /* -Wunitialized */ 1817b2fc195eSAndrew Gallatin struct mbuf *m_head = 0; 1818b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 18196d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1820b2fc195eSAndrew Gallatin int idx; 1821b2fc195eSAndrew Gallatin 1822b2fc195eSAndrew Gallatin 1823b2fc195eSAndrew Gallatin rx = &sc->rx_big; 1824b2fc195eSAndrew Gallatin ifp = sc->ifp; 1825b2fc195eSAndrew Gallatin while (len > 0) { 1826b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1827b2fc195eSAndrew Gallatin rx->cnt++; 1828b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1829b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1830b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 18316d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 1832b2fc195eSAndrew Gallatin goto drop; 1833b2fc195eSAndrew Gallatin } 1834b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1835b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1836b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1837b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1838b2fc195eSAndrew Gallatin 1839b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1840b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1841b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1842b2fc195eSAndrew Gallatin 1843b2fc195eSAndrew Gallatin /* chain multiple segments together */ 1844b2fc195eSAndrew Gallatin if (!m_head) { 1845b2fc195eSAndrew Gallatin m_head = m; 1846b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st bytes so that 1847b2fc195eSAndrew Gallatin * packet is properly aligned */ 18485e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1849b2fc195eSAndrew Gallatin m->m_pkthdr.len = len; 18505e7d8541SAndrew Gallatin m->m_len = sc->big_bytes - MXGEFW_PAD; 1851b2fc195eSAndrew Gallatin } else { 1852b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1853b2fc195eSAndrew Gallatin m->m_flags &= ~M_PKTHDR; 1854b2fc195eSAndrew Gallatin m_prev->m_next = m; 1855b2fc195eSAndrew Gallatin } 1856b2fc195eSAndrew Gallatin len -= m->m_len; 1857b2fc195eSAndrew Gallatin m_prev = m; 1858b2fc195eSAndrew Gallatin } 1859b2fc195eSAndrew Gallatin 1860b2fc195eSAndrew Gallatin /* trim trailing garbage from the last mbuf in the chain. If 1861b2fc195eSAndrew Gallatin * there is any garbage, len will be negative */ 1862b2fc195eSAndrew Gallatin m->m_len += len; 1863b2fc195eSAndrew Gallatin 1864b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 18655e7d8541SAndrew Gallatin if (sc->csum_flag) 18665e7d8541SAndrew Gallatin mxge_rx_csum(m_head, csum); 1867b2fc195eSAndrew Gallatin 1868b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1869b2fc195eSAndrew Gallatin m_head->m_pkthdr.rcvif = ifp; 1870b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1871b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m_head); 1872b2fc195eSAndrew Gallatin return; 1873b2fc195eSAndrew Gallatin 1874b2fc195eSAndrew Gallatin drop: 1875b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf(s) are re-cycled by running 1876b2fc195eSAndrew Gallatin every slot through the allocator */ 1877b2fc195eSAndrew Gallatin if (m_head) { 1878b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1879b2fc195eSAndrew Gallatin m_freem(m_head); 1880b2fc195eSAndrew Gallatin } else { 18815e7d8541SAndrew Gallatin len -= (sc->big_bytes + MXGEFW_PAD); 1882b2fc195eSAndrew Gallatin } 1883b2fc195eSAndrew Gallatin while ((int)len > 0) { 1884b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1885b2fc195eSAndrew Gallatin rx->cnt++; 1886b2fc195eSAndrew Gallatin m = rx->info[idx].m; 18876d87a65dSAndrew Gallatin if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) { 1888b2fc195eSAndrew Gallatin m_freem(m); 1889b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1890b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1891b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, 1892b2fc195eSAndrew Gallatin BUS_DMASYNC_POSTREAD); 1893b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1894b2fc195eSAndrew Gallatin 1895b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1896b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1897b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1898b2fc195eSAndrew Gallatin } 1899b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1900b2fc195eSAndrew Gallatin } 1901b2fc195eSAndrew Gallatin 1902b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1903b2fc195eSAndrew Gallatin 1904b2fc195eSAndrew Gallatin } 1905b2fc195eSAndrew Gallatin 1906b2fc195eSAndrew Gallatin static inline void 19075e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 1908b2fc195eSAndrew Gallatin { 1909b2fc195eSAndrew Gallatin struct ifnet *ifp; 1910b2fc195eSAndrew Gallatin struct mbuf *m; 19116d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1912b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 1913b2fc195eSAndrew Gallatin int idx; 1914b2fc195eSAndrew Gallatin 1915b2fc195eSAndrew Gallatin ifp = sc->ifp; 1916b2fc195eSAndrew Gallatin rx = &sc->rx_small; 1917b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1918b2fc195eSAndrew Gallatin rx->cnt++; 1919b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1920b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1921b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 19226d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 1923b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 1924b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1925b2fc195eSAndrew Gallatin return; 1926b2fc195eSAndrew Gallatin } 1927b2fc195eSAndrew Gallatin 1928b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1929b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1930b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1931b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1932b2fc195eSAndrew Gallatin 1933b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1934b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1935b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1936b2fc195eSAndrew Gallatin 1937b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 1938b2fc195eSAndrew Gallatin * aligned */ 19395e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1940b2fc195eSAndrew Gallatin 1941b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 19425e7d8541SAndrew Gallatin if (sc->csum_flag) 19435e7d8541SAndrew Gallatin mxge_rx_csum(m, csum); 1944b2fc195eSAndrew Gallatin 1945b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1946b2fc195eSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 1947b2fc195eSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 1948b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1949b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 1950b2fc195eSAndrew Gallatin } 1951b2fc195eSAndrew Gallatin 1952b2fc195eSAndrew Gallatin static inline void 19535e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 19545e7d8541SAndrew Gallatin { 19555e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 19565e7d8541SAndrew Gallatin int limit = 0; 19575e7d8541SAndrew Gallatin uint16_t length; 19585e7d8541SAndrew Gallatin uint16_t checksum; 19595e7d8541SAndrew Gallatin 19605e7d8541SAndrew Gallatin 19615e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 19625e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 19635e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 19645e7d8541SAndrew Gallatin checksum = ntohs(rx_done->entry[rx_done->idx].checksum); 1965b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 19665e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 19675e7d8541SAndrew Gallatin else 19685e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 19695e7d8541SAndrew Gallatin rx_done->cnt++; 19705e7d8541SAndrew Gallatin rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1); 19715e7d8541SAndrew Gallatin 19725e7d8541SAndrew Gallatin /* limit potential for livelock */ 19735e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * mxge_max_intr_slots)) 19745e7d8541SAndrew Gallatin break; 19755e7d8541SAndrew Gallatin 19765e7d8541SAndrew Gallatin } 19775e7d8541SAndrew Gallatin } 19785e7d8541SAndrew Gallatin 19795e7d8541SAndrew Gallatin 19805e7d8541SAndrew Gallatin static inline void 19816d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 1982b2fc195eSAndrew Gallatin { 1983b2fc195eSAndrew Gallatin struct ifnet *ifp; 19846d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1985b2fc195eSAndrew Gallatin struct mbuf *m; 1986b2fc195eSAndrew Gallatin bus_dmamap_t map; 19875e7d8541SAndrew Gallatin int idx, limit; 1988b2fc195eSAndrew Gallatin 19895e7d8541SAndrew Gallatin limit = 0; 1990b2fc195eSAndrew Gallatin tx = &sc->tx; 1991b2fc195eSAndrew Gallatin ifp = sc->ifp; 19925e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 1993b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 1994b2fc195eSAndrew Gallatin tx->done++; 1995b2fc195eSAndrew Gallatin m = tx->info[idx].m; 1996b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 1997b2fc195eSAndrew Gallatin segment per-mbuf */ 1998b2fc195eSAndrew Gallatin if (m != NULL) { 1999b2fc195eSAndrew Gallatin ifp->if_opackets++; 2000b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2001b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2002b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2003b2fc195eSAndrew Gallatin m_freem(m); 2004b2fc195eSAndrew Gallatin } 20055e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 20065e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 20075e7d8541SAndrew Gallatin tx->pkt_done++; 20085e7d8541SAndrew Gallatin } 20095e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 20105e7d8541SAndrew Gallatin 2 full tx rings per call */ 20115e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 20125e7d8541SAndrew Gallatin break; 2013b2fc195eSAndrew Gallatin } 2014b2fc195eSAndrew Gallatin 2015b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2016b2fc195eSAndrew Gallatin its OK to send packets */ 2017b2fc195eSAndrew Gallatin 2018b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2019b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2020b2fc195eSAndrew Gallatin mtx_lock(&sc->tx_lock); 2021b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 20226d87a65dSAndrew Gallatin mxge_start_locked(sc); 2023b2fc195eSAndrew Gallatin mtx_unlock(&sc->tx_lock); 2024b2fc195eSAndrew Gallatin } 2025b2fc195eSAndrew Gallatin } 2026b2fc195eSAndrew Gallatin 2027b2fc195eSAndrew Gallatin static void 20286d87a65dSAndrew Gallatin mxge_intr(void *arg) 2029b2fc195eSAndrew Gallatin { 20306d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 20315e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 20325e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 20335e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 20345e7d8541SAndrew Gallatin uint32_t send_done_count; 20355e7d8541SAndrew Gallatin uint8_t valid; 2036b2fc195eSAndrew Gallatin 2037b2fc195eSAndrew Gallatin 20385e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 20395e7d8541SAndrew Gallatin if (!stats->valid) { 20405e7d8541SAndrew Gallatin return; 2041b2fc195eSAndrew Gallatin } 20425e7d8541SAndrew Gallatin valid = stats->valid; 2043b2fc195eSAndrew Gallatin 2044dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 20455e7d8541SAndrew Gallatin /* lower legacy IRQ */ 20465e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 20475e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 20485e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 20495e7d8541SAndrew Gallatin stats->valid = 0; 2050dc8731d4SAndrew Gallatin } else { 2051dc8731d4SAndrew Gallatin stats->valid = 0; 2052dc8731d4SAndrew Gallatin } 2053dc8731d4SAndrew Gallatin 2054dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 20555e7d8541SAndrew Gallatin do { 20565e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 20575e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 20585e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 20595e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 20605e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 20615e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 20625e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2063b2fc195eSAndrew Gallatin } 20645e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2065b2fc195eSAndrew Gallatin 20665e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 20675e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 20685e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2069b2fc195eSAndrew Gallatin if (sc->link_state) { 20705e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 20715e7d8541SAndrew Gallatin if (mxge_verbose) 20725e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2073b2fc195eSAndrew Gallatin } else { 20745e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 20755e7d8541SAndrew Gallatin if (mxge_verbose) 20765e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2077b2fc195eSAndrew Gallatin } 2078b2fc195eSAndrew Gallatin } 2079b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2080b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2081b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2082b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 20835e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 20845e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 20855e7d8541SAndrew Gallatin } 20865e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2087b2fc195eSAndrew Gallatin } 2088b2fc195eSAndrew Gallatin 20895e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 20905e7d8541SAndrew Gallatin if (valid & 0x1) 20915e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 20925e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2093b2fc195eSAndrew Gallatin } 2094b2fc195eSAndrew Gallatin 2095b2fc195eSAndrew Gallatin static void 20966d87a65dSAndrew Gallatin mxge_watchdog(struct ifnet *ifp) 2097b2fc195eSAndrew Gallatin { 2098b2fc195eSAndrew Gallatin printf("%s called\n", __FUNCTION__); 2099b2fc195eSAndrew Gallatin } 2100b2fc195eSAndrew Gallatin 2101b2fc195eSAndrew Gallatin static void 21026d87a65dSAndrew Gallatin mxge_init(void *arg) 2103b2fc195eSAndrew Gallatin { 2104b2fc195eSAndrew Gallatin } 2105b2fc195eSAndrew Gallatin 2106b2fc195eSAndrew Gallatin 2107b2fc195eSAndrew Gallatin 2108b2fc195eSAndrew Gallatin static void 21096d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2110b2fc195eSAndrew Gallatin { 2111b2fc195eSAndrew Gallatin int i; 2112b2fc195eSAndrew Gallatin 2113b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2114b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2115b2fc195eSAndrew Gallatin continue; 2116b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2117b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2118b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2119b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2120b2fc195eSAndrew Gallatin } 2121b2fc195eSAndrew Gallatin 2122b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2123b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2124b2fc195eSAndrew Gallatin continue; 2125b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2126b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2127b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2128b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2129b2fc195eSAndrew Gallatin } 2130b2fc195eSAndrew Gallatin 2131b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2132b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2133b2fc195eSAndrew Gallatin continue; 2134b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2135b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2136b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2137b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2138b2fc195eSAndrew Gallatin } 2139b2fc195eSAndrew Gallatin } 2140b2fc195eSAndrew Gallatin 2141b2fc195eSAndrew Gallatin static void 21426d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2143b2fc195eSAndrew Gallatin { 2144b2fc195eSAndrew Gallatin int i; 2145b2fc195eSAndrew Gallatin 2146aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2147b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2148aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2149aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2150b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2151b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2152b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2153b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2154b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2155b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2156b2fc195eSAndrew Gallatin if (sc->tx.info[i].map != NULL) 2157b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2158b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2159b2fc195eSAndrew Gallatin } 2160b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2161b2fc195eSAndrew Gallatin } 2162b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2163b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2164b2fc195eSAndrew Gallatin if (sc->rx_small.info[i].map != NULL) 2165b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2166b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2167b2fc195eSAndrew Gallatin } 2168b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2169b2fc195eSAndrew Gallatin } 2170b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2171b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2172b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].map != NULL) 2173b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2174b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2175b2fc195eSAndrew Gallatin } 2176b2fc195eSAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2177b2fc195eSAndrew Gallatin } 2178b2fc195eSAndrew Gallatin if (sc->rx_big.extra_map != NULL) 2179b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2180b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2181b2fc195eSAndrew Gallatin if (sc->rx_small.extra_map != NULL) 2182b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2183b2fc195eSAndrew Gallatin sc->rx_small.extra_map); 2184b2fc195eSAndrew Gallatin if (sc->tx.dmat != NULL) 2185b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2186b2fc195eSAndrew Gallatin if (sc->rx_small.dmat != NULL) 2187b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2188b2fc195eSAndrew Gallatin if (sc->rx_big.dmat != NULL) 2189b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2190b2fc195eSAndrew Gallatin } 2191b2fc195eSAndrew Gallatin 2192b2fc195eSAndrew Gallatin static int 21936d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2194b2fc195eSAndrew Gallatin { 21956d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2196b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2197b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2198b2fc195eSAndrew Gallatin int i, err; 2199b2fc195eSAndrew Gallatin unsigned long bytes; 2200b2fc195eSAndrew Gallatin 2201b2fc195eSAndrew Gallatin /* get ring sizes */ 22025e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2203b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 22045e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2205b2fc195eSAndrew Gallatin if (err != 0) { 2206b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2207b2fc195eSAndrew Gallatin goto abort_with_nothing; 2208b2fc195eSAndrew Gallatin } 2209b2fc195eSAndrew Gallatin 2210b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2211b2fc195eSAndrew Gallatin 2212b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2213b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 2214b2fc195eSAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 221576bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 221676bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2217b2fc195eSAndrew Gallatin 2218b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2219b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2220b2fc195eSAndrew Gallatin 2221b2fc195eSAndrew Gallatin err = ENOMEM; 2222b2fc195eSAndrew Gallatin 2223b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2224b2fc195eSAndrew Gallatin bytes = 8 + 22255e7d8541SAndrew Gallatin sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4); 2226b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2227b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2228b2fc195eSAndrew Gallatin goto abort_with_nothing; 2229b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2230b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2231b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2232b2fc195eSAndrew Gallatin 2233aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2234aed8e389SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC; 2235aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2236aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2237aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2238aed8e389SAndrew Gallatin goto abort_with_alloc; 2239aed8e389SAndrew Gallatin 2240b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2241b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2242b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2243b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2244b2fc195eSAndrew Gallatin goto abort_with_alloc; 2245b2fc195eSAndrew Gallatin 2246b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2247b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2248b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2249b2fc195eSAndrew Gallatin goto abort_with_alloc; 2250b2fc195eSAndrew Gallatin 2251b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2252b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2253b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2254b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2255b2fc195eSAndrew Gallatin goto abort_with_alloc; 2256b2fc195eSAndrew Gallatin 2257b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2258b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2259b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2260b2fc195eSAndrew Gallatin goto abort_with_alloc; 2261b2fc195eSAndrew Gallatin 2262b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2263b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2264b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2265b2fc195eSAndrew Gallatin goto abort_with_alloc; 2266b2fc195eSAndrew Gallatin 2267b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2268b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2269b2fc195eSAndrew Gallatin 1, /* alignment */ 2270b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2271b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2272b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2273b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2274aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2275aed8e389SAndrew Gallatin MXGE_MAX_SEND_DESC/2, /* num segs */ 2276b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2277b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2278b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2279b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2280b2fc195eSAndrew Gallatin 2281b2fc195eSAndrew Gallatin if (err != 0) { 2282b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2283b2fc195eSAndrew Gallatin err); 2284b2fc195eSAndrew Gallatin goto abort_with_alloc; 2285b2fc195eSAndrew Gallatin } 2286b2fc195eSAndrew Gallatin 2287b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2288b2fc195eSAndrew Gallatin 1, /* alignment */ 2289b2fc195eSAndrew Gallatin 4096, /* boundary */ 2290b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2291b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2292b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2293b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2294b2fc195eSAndrew Gallatin 1, /* num segs */ 2295b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2296b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2297b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2298b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2299b2fc195eSAndrew Gallatin if (err != 0) { 2300b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2301b2fc195eSAndrew Gallatin err); 2302b2fc195eSAndrew Gallatin goto abort_with_alloc; 2303b2fc195eSAndrew Gallatin } 2304b2fc195eSAndrew Gallatin 2305b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2306b2fc195eSAndrew Gallatin 1, /* alignment */ 2307b2fc195eSAndrew Gallatin 4096, /* boundary */ 2308b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2309b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2310b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2311b2fc195eSAndrew Gallatin 4096, /* maxsize */ 2312b2fc195eSAndrew Gallatin 1, /* num segs */ 2313b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2314b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2315b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2316b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2317b2fc195eSAndrew Gallatin if (err != 0) { 2318b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2319b2fc195eSAndrew Gallatin err); 2320b2fc195eSAndrew Gallatin goto abort_with_alloc; 2321b2fc195eSAndrew Gallatin } 2322b2fc195eSAndrew Gallatin 2323b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2324b2fc195eSAndrew Gallatin in each ring */ 2325b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2326b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2327b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2328b2fc195eSAndrew Gallatin if (err != 0) { 2329b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2330b2fc195eSAndrew Gallatin err); 2331b2fc195eSAndrew Gallatin goto abort_with_alloc; 2332b2fc195eSAndrew Gallatin } 2333b2fc195eSAndrew Gallatin } 2334b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2335b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2336b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2337b2fc195eSAndrew Gallatin if (err != 0) { 2338b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2339b2fc195eSAndrew Gallatin err); 2340b2fc195eSAndrew Gallatin goto abort_with_alloc; 2341b2fc195eSAndrew Gallatin } 2342b2fc195eSAndrew Gallatin } 2343b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2344b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2345b2fc195eSAndrew Gallatin if (err != 0) { 2346b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2347b2fc195eSAndrew Gallatin err); 2348b2fc195eSAndrew Gallatin goto abort_with_alloc; 2349b2fc195eSAndrew Gallatin } 2350b2fc195eSAndrew Gallatin 2351b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2352b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2353b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2354b2fc195eSAndrew Gallatin if (err != 0) { 2355b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2356b2fc195eSAndrew Gallatin err); 2357b2fc195eSAndrew Gallatin goto abort_with_alloc; 2358b2fc195eSAndrew Gallatin } 2359b2fc195eSAndrew Gallatin } 2360b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2361b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2362b2fc195eSAndrew Gallatin if (err != 0) { 2363b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2364b2fc195eSAndrew Gallatin err); 2365b2fc195eSAndrew Gallatin goto abort_with_alloc; 2366b2fc195eSAndrew Gallatin } 2367b2fc195eSAndrew Gallatin return 0; 2368b2fc195eSAndrew Gallatin 2369b2fc195eSAndrew Gallatin abort_with_alloc: 23706d87a65dSAndrew Gallatin mxge_free_rings(sc); 2371b2fc195eSAndrew Gallatin 2372b2fc195eSAndrew Gallatin abort_with_nothing: 2373b2fc195eSAndrew Gallatin return err; 2374b2fc195eSAndrew Gallatin } 2375b2fc195eSAndrew Gallatin 2376b2fc195eSAndrew Gallatin static int 23776d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2378b2fc195eSAndrew Gallatin { 23796d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2380b2fc195eSAndrew Gallatin int i, err; 2381b2fc195eSAndrew Gallatin bus_dmamap_t map; 23820fa7f681SAndrew Gallatin bus_addr_t bus; 2383b2fc195eSAndrew Gallatin 2384b2fc195eSAndrew Gallatin 23857d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 23867d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 23877d542e2dSAndrew Gallatin 23886d87a65dSAndrew Gallatin err = mxge_reset(sc); 2389b2fc195eSAndrew Gallatin if (err != 0) { 2390b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2391b2fc195eSAndrew Gallatin return EIO; 2392b2fc195eSAndrew Gallatin } 2393b2fc195eSAndrew Gallatin 2394b2fc195eSAndrew Gallatin if (MCLBYTES >= 23955e7d8541SAndrew Gallatin sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD) 2396b2fc195eSAndrew Gallatin sc->big_bytes = MCLBYTES; 2397b2fc195eSAndrew Gallatin else 2398b2fc195eSAndrew Gallatin sc->big_bytes = MJUMPAGESIZE; 2399b2fc195eSAndrew Gallatin 24006d87a65dSAndrew Gallatin err = mxge_alloc_rings(sc); 2401b2fc195eSAndrew Gallatin if (err != 0) { 2402b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 2403b2fc195eSAndrew Gallatin return err; 2404b2fc195eSAndrew Gallatin } 2405b2fc195eSAndrew Gallatin 2406b2fc195eSAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 2407b2fc195eSAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 24086d87a65dSAndrew Gallatin mxge_intr, sc, &sc->ih); 2409b2fc195eSAndrew Gallatin if (err != 0) { 2410b2fc195eSAndrew Gallatin goto abort_with_rings; 2411b2fc195eSAndrew Gallatin } 2412b2fc195eSAndrew Gallatin 2413b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2414b2fc195eSAndrew Gallatin 24155e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2416b2fc195eSAndrew Gallatin sc->tx.lanai = 2417b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 24186d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 24195e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2420b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2421b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 24225e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2423b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2424b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2425b2fc195eSAndrew Gallatin 2426b2fc195eSAndrew Gallatin if (err != 0) { 2427b2fc195eSAndrew Gallatin device_printf(sc->dev, 2428b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2429b2fc195eSAndrew Gallatin err = EIO; 2430b2fc195eSAndrew Gallatin goto abort_with_irq; 2431b2fc195eSAndrew Gallatin } 2432b2fc195eSAndrew Gallatin 2433b2fc195eSAndrew Gallatin if (sc->wc) { 24340fa7f681SAndrew Gallatin sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4; 24350fa7f681SAndrew Gallatin sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL; 24360fa7f681SAndrew Gallatin sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG; 2437b2fc195eSAndrew Gallatin } else { 2438b2fc195eSAndrew Gallatin sc->tx.wc_fifo = 0; 2439b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = 0; 2440b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = 0; 2441b2fc195eSAndrew Gallatin } 2442b2fc195eSAndrew Gallatin 2443b2fc195eSAndrew Gallatin 2444b2fc195eSAndrew Gallatin /* stock receive rings */ 2445b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2446b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 24476d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2448b2fc195eSAndrew Gallatin if (err) { 2449b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2450b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2451b2fc195eSAndrew Gallatin goto abort; 2452b2fc195eSAndrew Gallatin } 2453b2fc195eSAndrew Gallatin } 2454b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2455b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 24566d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2457b2fc195eSAndrew Gallatin if (err) { 2458b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2459b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2460b2fc195eSAndrew Gallatin goto abort; 2461b2fc195eSAndrew Gallatin } 2462b2fc195eSAndrew Gallatin } 2463b2fc195eSAndrew Gallatin 2464b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2465b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2466b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2467b2fc195eSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN; 24685e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2469b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 24705e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2471b2fc195eSAndrew Gallatin &cmd); 2472b2fc195eSAndrew Gallatin cmd.data0 = sc->big_bytes; 24735e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 24740fa7f681SAndrew Gallatin 24750fa7f681SAndrew Gallatin if (err != 0) { 24760fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 24770fa7f681SAndrew Gallatin goto abort; 24780fa7f681SAndrew Gallatin } 24790fa7f681SAndrew Gallatin 2480b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 24816d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 24826d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 24830fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 24840fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 24850fa7f681SAndrew Gallatin 24860fa7f681SAndrew Gallatin if (err != 0) { 24870fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 24880fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 24890fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 24900fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 24910fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 24920fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 24930fa7f681SAndrew Gallatin &cmd); 24940fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 24950fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 24960fa7f681SAndrew Gallatin } else { 24970fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 24980fa7f681SAndrew Gallatin } 2499b2fc195eSAndrew Gallatin 2500b2fc195eSAndrew Gallatin if (err != 0) { 2501b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2502b2fc195eSAndrew Gallatin goto abort; 2503b2fc195eSAndrew Gallatin } 2504b2fc195eSAndrew Gallatin 2505b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 25065e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2507b2fc195eSAndrew Gallatin if (err) { 2508b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2509b2fc195eSAndrew Gallatin goto abort; 2510b2fc195eSAndrew Gallatin } 2511b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2512b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2513b2fc195eSAndrew Gallatin 2514b2fc195eSAndrew Gallatin return 0; 2515b2fc195eSAndrew Gallatin 2516b2fc195eSAndrew Gallatin 2517b2fc195eSAndrew Gallatin abort: 25186d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2519b2fc195eSAndrew Gallatin abort_with_irq: 2520b2fc195eSAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 2521b2fc195eSAndrew Gallatin abort_with_rings: 25226d87a65dSAndrew Gallatin mxge_free_rings(sc); 2523b2fc195eSAndrew Gallatin return err; 2524b2fc195eSAndrew Gallatin } 2525b2fc195eSAndrew Gallatin 2526b2fc195eSAndrew Gallatin static int 25276d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2528b2fc195eSAndrew Gallatin { 25296d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2530b2fc195eSAndrew Gallatin int err, old_down_cnt; 2531b2fc195eSAndrew Gallatin 2532b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2533b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2534b2fc195eSAndrew Gallatin mb(); 25355e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2536b2fc195eSAndrew Gallatin if (err) { 2537b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2538b2fc195eSAndrew Gallatin } 2539b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2540b2fc195eSAndrew Gallatin /* wait for down irq */ 25416d87a65dSAndrew Gallatin (void)tsleep(&sc->down_cnt, PWAIT, "down mxge", hz); 2542b2fc195eSAndrew Gallatin } 2543b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2544b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2545b2fc195eSAndrew Gallatin } 2546b2fc195eSAndrew Gallatin if (sc->ih != NULL) 2547b2fc195eSAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 25486d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 25496d87a65dSAndrew Gallatin mxge_free_rings(sc); 2550b2fc195eSAndrew Gallatin return 0; 2551b2fc195eSAndrew Gallatin } 2552b2fc195eSAndrew Gallatin 2553b2fc195eSAndrew Gallatin 2554b2fc195eSAndrew Gallatin static int 25556d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2556b2fc195eSAndrew Gallatin { 2557b2fc195eSAndrew Gallatin return EINVAL; 2558b2fc195eSAndrew Gallatin } 2559b2fc195eSAndrew Gallatin 2560b2fc195eSAndrew Gallatin static int 25616d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2562b2fc195eSAndrew Gallatin { 2563b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2564b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2565b2fc195eSAndrew Gallatin int err = 0; 2566b2fc195eSAndrew Gallatin 2567b2fc195eSAndrew Gallatin 2568b2fc195eSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN; 25696d87a65dSAndrew Gallatin if ((real_mtu > MXGE_MAX_ETHER_MTU) || 2570b2fc195eSAndrew Gallatin real_mtu < 60) 2571b2fc195eSAndrew Gallatin return EINVAL; 2572b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2573b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2574b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2575b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 25766d87a65dSAndrew Gallatin mxge_close(sc); 25776d87a65dSAndrew Gallatin err = mxge_open(sc); 2578b2fc195eSAndrew Gallatin if (err != 0) { 2579b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 25806d87a65dSAndrew Gallatin mxge_close(sc); 25816d87a65dSAndrew Gallatin (void) mxge_open(sc); 2582b2fc195eSAndrew Gallatin } 2583b2fc195eSAndrew Gallatin } 2584b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2585b2fc195eSAndrew Gallatin return err; 2586b2fc195eSAndrew Gallatin } 2587b2fc195eSAndrew Gallatin 2588b2fc195eSAndrew Gallatin static void 25896d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 2590b2fc195eSAndrew Gallatin { 25916d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2592b2fc195eSAndrew Gallatin 2593b2fc195eSAndrew Gallatin 2594b2fc195eSAndrew Gallatin if (sc == NULL) 2595b2fc195eSAndrew Gallatin return; 2596b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 2597b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 2598b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 2599b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 2600b2fc195eSAndrew Gallatin } 2601b2fc195eSAndrew Gallatin 2602b2fc195eSAndrew Gallatin static int 26036d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2604b2fc195eSAndrew Gallatin { 26056d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2606b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 2607b2fc195eSAndrew Gallatin int err, mask; 2608b2fc195eSAndrew Gallatin 2609b2fc195eSAndrew Gallatin err = 0; 2610b2fc195eSAndrew Gallatin switch (command) { 2611b2fc195eSAndrew Gallatin case SIOCSIFADDR: 2612b2fc195eSAndrew Gallatin case SIOCGIFADDR: 2613b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 2614b2fc195eSAndrew Gallatin break; 2615b2fc195eSAndrew Gallatin 2616b2fc195eSAndrew Gallatin case SIOCSIFMTU: 26176d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 2618b2fc195eSAndrew Gallatin break; 2619b2fc195eSAndrew Gallatin 2620b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 2621b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2622b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 2623b2fc195eSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 26246d87a65dSAndrew Gallatin err = mxge_open(sc); 26250fa7f681SAndrew Gallatin else { 26260fa7f681SAndrew Gallatin /* take care of promis can allmulti 26270fa7f681SAndrew Gallatin flag chages */ 26280fa7f681SAndrew Gallatin mxge_change_promisc(sc, 26290fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 26300fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 26310fa7f681SAndrew Gallatin } 2632b2fc195eSAndrew Gallatin } else { 2633b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 26346d87a65dSAndrew Gallatin mxge_close(sc); 2635b2fc195eSAndrew Gallatin } 2636b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2637b2fc195eSAndrew Gallatin break; 2638b2fc195eSAndrew Gallatin 2639b2fc195eSAndrew Gallatin case SIOCADDMULTI: 2640b2fc195eSAndrew Gallatin case SIOCDELMULTI: 26410fa7f681SAndrew Gallatin sx_xlock(&sc->driver_lock); 26420fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 26430fa7f681SAndrew Gallatin sx_xunlock(&sc->driver_lock); 2644b2fc195eSAndrew Gallatin break; 2645b2fc195eSAndrew Gallatin 2646b2fc195eSAndrew Gallatin case SIOCSIFCAP: 2647b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2648b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2649b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 2650b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 2651aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 2652aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 2653aed8e389SAndrew Gallatin | CSUM_TSO); 2654b2fc195eSAndrew Gallatin } else { 2655b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 2656b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 2657b2fc195eSAndrew Gallatin } 2658b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 2659b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 2660b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 26615e7d8541SAndrew Gallatin sc->csum_flag = 0; 2662b2fc195eSAndrew Gallatin } else { 2663b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 26645e7d8541SAndrew Gallatin sc->csum_flag = 1; 2665b2fc195eSAndrew Gallatin } 2666b2fc195eSAndrew Gallatin } 2667aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 2668aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 2669aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 2670aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 2671aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 2672aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 2673aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 2674aed8e389SAndrew Gallatin } else { 2675aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 2676aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 2677aed8e389SAndrew Gallatin err = EINVAL; 2678aed8e389SAndrew Gallatin } 2679aed8e389SAndrew Gallatin } 2680b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2681b2fc195eSAndrew Gallatin break; 2682b2fc195eSAndrew Gallatin 2683b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 2684b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 2685b2fc195eSAndrew Gallatin &sc->media, command); 2686b2fc195eSAndrew Gallatin break; 2687b2fc195eSAndrew Gallatin 2688b2fc195eSAndrew Gallatin default: 2689b2fc195eSAndrew Gallatin err = ENOTTY; 2690b2fc195eSAndrew Gallatin } 2691b2fc195eSAndrew Gallatin return err; 2692b2fc195eSAndrew Gallatin } 2693b2fc195eSAndrew Gallatin 2694b2fc195eSAndrew Gallatin static void 26956d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 2696b2fc195eSAndrew Gallatin { 2697b2fc195eSAndrew Gallatin 26986d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 26996d87a65dSAndrew Gallatin &mxge_flow_control); 27006d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 27016d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 27026d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 27036d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 2704d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 2705d91b1b49SAndrew Gallatin &mxge_force_firmware); 27065e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 27075e7d8541SAndrew Gallatin &mxge_deassert_wait); 27085e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 27095e7d8541SAndrew Gallatin &mxge_verbose); 2710b2fc195eSAndrew Gallatin 27115e7d8541SAndrew Gallatin if (bootverbose) 27125e7d8541SAndrew Gallatin mxge_verbose = 1; 27136d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 27146d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 27156d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 2716b2fc195eSAndrew Gallatin } 2717b2fc195eSAndrew Gallatin 2718b2fc195eSAndrew Gallatin static int 27196d87a65dSAndrew Gallatin mxge_attach(device_t dev) 2720b2fc195eSAndrew Gallatin { 27216d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2722b2fc195eSAndrew Gallatin struct ifnet *ifp; 2723b2fc195eSAndrew Gallatin size_t bytes; 2724d91b1b49SAndrew Gallatin int count, rid, err, reg; 2725d91b1b49SAndrew Gallatin uint16_t cmd, pectl, lnk; 2726b2fc195eSAndrew Gallatin 2727b2fc195eSAndrew Gallatin sc->dev = dev; 27286d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 2729b2fc195eSAndrew Gallatin 2730b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 2731b2fc195eSAndrew Gallatin 1, /* alignment */ 2732b2fc195eSAndrew Gallatin 4096, /* boundary */ 2733b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2734b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2735b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2736aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 27375e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 2738b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2739b2fc195eSAndrew Gallatin 0, /* flags */ 2740b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2741b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 2742b2fc195eSAndrew Gallatin 2743b2fc195eSAndrew Gallatin if (err != 0) { 2744b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 2745b2fc195eSAndrew Gallatin err); 2746b2fc195eSAndrew Gallatin goto abort_with_nothing; 2747b2fc195eSAndrew Gallatin } 2748b2fc195eSAndrew Gallatin 2749b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 2750b2fc195eSAndrew Gallatin if (ifp == NULL) { 2751b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 2752b2fc195eSAndrew Gallatin err = ENOSPC; 2753b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 2754b2fc195eSAndrew Gallatin } 2755b2fc195eSAndrew Gallatin mtx_init(&sc->cmd_lock, NULL, 2756b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2757b2fc195eSAndrew Gallatin mtx_init(&sc->tx_lock, device_get_nameunit(dev), 2758b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2759b2fc195eSAndrew Gallatin sx_init(&sc->driver_lock, device_get_nameunit(dev)); 2760b2fc195eSAndrew Gallatin 2761d91b1b49SAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2762d91b1b49SAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2763d91b1b49SAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2764d91b1b49SAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2765d91b1b49SAndrew Gallatin 2766d91b1b49SAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2767d91b1b49SAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2768d91b1b49SAndrew Gallatin pci_write_config(dev, reg + 0x8, 2, pectl); 2769d91b1b49SAndrew Gallatin } 2770d91b1b49SAndrew Gallatin 2771b2fc195eSAndrew Gallatin /* Enable DMA and Memory space access */ 2772b2fc195eSAndrew Gallatin pci_enable_busmaster(dev); 2773b2fc195eSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2774b2fc195eSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2775b2fc195eSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2776b2fc195eSAndrew Gallatin 2777b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 2778b2fc195eSAndrew Gallatin rid = PCIR_BARS; 2779b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 2780b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 2781b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 2782b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 2783b2fc195eSAndrew Gallatin err = ENXIO; 2784b2fc195eSAndrew Gallatin goto abort_with_lock; 2785b2fc195eSAndrew Gallatin } 2786b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 2787b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 2788b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 2789b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 2790b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 2791b2fc195eSAndrew Gallatin err = ENXIO; 2792b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2793b2fc195eSAndrew Gallatin } 2794b2fc195eSAndrew Gallatin 2795b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 2796b2fc195eSAndrew Gallatin lanai SRAM */ 27976d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 2798b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 2799b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 28006d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 2801b2fc195eSAndrew Gallatin sc->eeprom_strings, 28026d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 28036d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 2804b2fc195eSAndrew Gallatin if (err != 0) 2805b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2806b2fc195eSAndrew Gallatin 2807b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 28086d87a65dSAndrew Gallatin mxge_enable_wc(sc); 2809b2fc195eSAndrew Gallatin 2810b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 28116d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 28126d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 2813b2fc195eSAndrew Gallatin if (err != 0) 2814b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2815b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 28166d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 2817b2fc195eSAndrew Gallatin if (err != 0) 2818b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 2819b2fc195eSAndrew Gallatin 28206d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 2821b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 2822b2fc195eSAndrew Gallatin if (err != 0) 2823b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 28245e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 2825b2fc195eSAndrew Gallatin 2826b2fc195eSAndrew Gallatin 2827b2fc195eSAndrew Gallatin /* allocate interrupt queues */ 28285e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry); 28295e7d8541SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2830b2fc195eSAndrew Gallatin if (err != 0) 28315e7d8541SAndrew Gallatin goto abort_with_fw_stats; 28325e7d8541SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 28335e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2834dc8731d4SAndrew Gallatin 2835b2fc195eSAndrew Gallatin /* Add our ithread */ 2836dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 2837dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 2838dc8731d4SAndrew Gallatin rid = 1; 2839dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 2840dc8731d4SAndrew Gallatin } else { 2841b2fc195eSAndrew Gallatin rid = 0; 2842dc8731d4SAndrew Gallatin } 2843b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 2844b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 2845b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 2846b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 28475e7d8541SAndrew Gallatin goto abort_with_rx_done; 2848b2fc195eSAndrew Gallatin } 2849d91b1b49SAndrew Gallatin if (mxge_verbose) 2850dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 2851dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 2852dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 2853b2fc195eSAndrew Gallatin /* load the firmware */ 28546d87a65dSAndrew Gallatin mxge_select_firmware(sc); 2855b2fc195eSAndrew Gallatin 28566d87a65dSAndrew Gallatin err = mxge_load_firmware(sc); 2857b2fc195eSAndrew Gallatin if (err != 0) 2858b2fc195eSAndrew Gallatin goto abort_with_irq_res; 28595e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 28606d87a65dSAndrew Gallatin err = mxge_reset(sc); 2861b2fc195eSAndrew Gallatin if (err != 0) 2862b2fc195eSAndrew Gallatin goto abort_with_irq_res; 2863b2fc195eSAndrew Gallatin 2864b2fc195eSAndrew Gallatin /* hook into the network stack */ 2865b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 2866b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 2867aed8e389SAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4; 2868aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 2869b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 28705e7d8541SAndrew Gallatin sc->csum_flag = 1; 28716d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 2872b2fc195eSAndrew Gallatin ifp->if_softc = sc; 2873b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 28746d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 28756d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 28766d87a65dSAndrew Gallatin ifp->if_watchdog = mxge_watchdog; 2877b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 2878b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 28796d87a65dSAndrew Gallatin ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; 2880b2fc195eSAndrew Gallatin 2881b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 28826d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 28836d87a65dSAndrew Gallatin mxge_media_status); 2884b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 28856d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 2886b2fc195eSAndrew Gallatin return 0; 2887b2fc195eSAndrew Gallatin 2888b2fc195eSAndrew Gallatin abort_with_irq_res: 2889dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 2890dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 2891dc8731d4SAndrew Gallatin if (sc->msi_enabled) 2892dc8731d4SAndrew Gallatin pci_release_msi(dev); 28935e7d8541SAndrew Gallatin abort_with_rx_done: 28945e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 28955e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 28965e7d8541SAndrew Gallatin abort_with_fw_stats: 28976d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 2898b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 28996d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 2900b2fc195eSAndrew Gallatin abort_with_cmd_dma: 29016d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 2902b2fc195eSAndrew Gallatin abort_with_mem_res: 2903b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 2904b2fc195eSAndrew Gallatin abort_with_lock: 2905b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 2906b2fc195eSAndrew Gallatin mtx_destroy(&sc->cmd_lock); 2907b2fc195eSAndrew Gallatin mtx_destroy(&sc->tx_lock); 2908b2fc195eSAndrew Gallatin sx_destroy(&sc->driver_lock); 2909b2fc195eSAndrew Gallatin if_free(ifp); 2910b2fc195eSAndrew Gallatin abort_with_parent_dmat: 2911b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 2912b2fc195eSAndrew Gallatin 2913b2fc195eSAndrew Gallatin abort_with_nothing: 2914b2fc195eSAndrew Gallatin return err; 2915b2fc195eSAndrew Gallatin } 2916b2fc195eSAndrew Gallatin 2917b2fc195eSAndrew Gallatin static int 29186d87a65dSAndrew Gallatin mxge_detach(device_t dev) 2919b2fc195eSAndrew Gallatin { 29206d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2921b2fc195eSAndrew Gallatin 2922b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2923b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 29246d87a65dSAndrew Gallatin mxge_close(sc); 2925b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2926b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 2927091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 2928dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 2929dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 2930dc8731d4SAndrew Gallatin if (sc->msi_enabled) 2931dc8731d4SAndrew Gallatin pci_release_msi(dev); 2932dc8731d4SAndrew Gallatin 29335e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 29345e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 29356d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 29366d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 29376d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 2938b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 2939b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 2940b2fc195eSAndrew Gallatin mtx_destroy(&sc->cmd_lock); 2941b2fc195eSAndrew Gallatin mtx_destroy(&sc->tx_lock); 2942b2fc195eSAndrew Gallatin sx_destroy(&sc->driver_lock); 2943b2fc195eSAndrew Gallatin if_free(sc->ifp); 2944b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 2945b2fc195eSAndrew Gallatin return 0; 2946b2fc195eSAndrew Gallatin } 2947b2fc195eSAndrew Gallatin 2948b2fc195eSAndrew Gallatin static int 29496d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 2950b2fc195eSAndrew Gallatin { 2951b2fc195eSAndrew Gallatin return 0; 2952b2fc195eSAndrew Gallatin } 2953b2fc195eSAndrew Gallatin 2954b2fc195eSAndrew Gallatin /* 2955b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 2956b2fc195eSAndrew Gallatin 2957b2fc195eSAndrew Gallatin Local Variables: 2958b2fc195eSAndrew Gallatin c-file-style:"linux" 2959b2fc195eSAndrew Gallatin tab-width:8 2960b2fc195eSAndrew Gallatin End: 2961b2fc195eSAndrew Gallatin */ 2962