16d87a65dSAndrew Gallatin /****************************************************************************** 2b2fc195eSAndrew Gallatin 3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc. 4b2fc195eSAndrew Gallatin All rights reserved. 5b2fc195eSAndrew Gallatin 6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without 7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met: 8b2fc195eSAndrew Gallatin 9b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice, 10b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer. 11b2fc195eSAndrew Gallatin 12b2fc195eSAndrew Gallatin 2. Redistributions in binary form must reproduce the above copyright 13b2fc195eSAndrew Gallatin notice, this list of conditions and the following disclaimer in the 14b2fc195eSAndrew Gallatin documentation and/or other materials provided with the distribution. 15b2fc195eSAndrew Gallatin 16b2fc195eSAndrew Gallatin 3. Neither the name of the Myricom Inc, nor the names of its 17b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 18b2fc195eSAndrew Gallatin this software without specific prior written permission. 19b2fc195eSAndrew Gallatin 20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 31b2fc195eSAndrew Gallatin 32b2fc195eSAndrew Gallatin ***************************************************************************/ 33b2fc195eSAndrew Gallatin 34b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 36b2fc195eSAndrew Gallatin 37b2fc195eSAndrew Gallatin #include <sys/param.h> 38b2fc195eSAndrew Gallatin #include <sys/systm.h> 39b2fc195eSAndrew Gallatin #include <sys/linker.h> 40b2fc195eSAndrew Gallatin #include <sys/firmware.h> 41b2fc195eSAndrew Gallatin #include <sys/endian.h> 42b2fc195eSAndrew Gallatin #include <sys/sockio.h> 43b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 44b2fc195eSAndrew Gallatin #include <sys/malloc.h> 45b2fc195eSAndrew Gallatin #include <sys/kdb.h> 46b2fc195eSAndrew Gallatin #include <sys/kernel.h> 474e7f640dSJohn Baldwin #include <sys/lock.h> 48b2fc195eSAndrew Gallatin #include <sys/module.h> 49b2fc195eSAndrew Gallatin #include <sys/memrange.h> 50b2fc195eSAndrew Gallatin #include <sys/socket.h> 51b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 52b2fc195eSAndrew Gallatin #include <sys/sx.h> 53b2fc195eSAndrew Gallatin 54b2fc195eSAndrew Gallatin #include <net/if.h> 55b2fc195eSAndrew Gallatin #include <net/if_arp.h> 56b2fc195eSAndrew Gallatin #include <net/ethernet.h> 57b2fc195eSAndrew Gallatin #include <net/if_dl.h> 58b2fc195eSAndrew Gallatin #include <net/if_media.h> 59b2fc195eSAndrew Gallatin 60b2fc195eSAndrew Gallatin #include <net/bpf.h> 61b2fc195eSAndrew Gallatin 62b2fc195eSAndrew Gallatin #include <net/if_types.h> 63b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h> 64b2fc195eSAndrew Gallatin #include <net/zlib.h> 65b2fc195eSAndrew Gallatin 66b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 67b2fc195eSAndrew Gallatin #include <netinet/in.h> 68b2fc195eSAndrew Gallatin #include <netinet/ip.h> 69aed8e389SAndrew Gallatin #include <netinet/tcp.h> 70b2fc195eSAndrew Gallatin 71b2fc195eSAndrew Gallatin #include <machine/bus.h> 72b2fc195eSAndrew Gallatin #include <machine/resource.h> 73b2fc195eSAndrew Gallatin #include <sys/bus.h> 74b2fc195eSAndrew Gallatin #include <sys/rman.h> 75b2fc195eSAndrew Gallatin 76b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 77b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 78b2fc195eSAndrew Gallatin 79b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 80b2fc195eSAndrew Gallatin #include <vm/pmap.h> 81b2fc195eSAndrew Gallatin 826d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 836d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 846d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 85b2fc195eSAndrew Gallatin 86b2fc195eSAndrew Gallatin /* tunable params */ 876d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 88d91b1b49SAndrew Gallatin static int mxge_force_firmware = 0; 895e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024; 906d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 915e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 926d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 935e7d8541SAndrew Gallatin static int mxge_verbose = 0; 94dce01b9bSAndrew Gallatin static int mxge_ticks; 956d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 966d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 97b2fc195eSAndrew Gallatin 986d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 996d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 1006d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 1016d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 1026d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 103b2fc195eSAndrew Gallatin 1046d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 105b2fc195eSAndrew Gallatin { 106b2fc195eSAndrew Gallatin /* Device interface */ 1076d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1086d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1096d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1106d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 111b2fc195eSAndrew Gallatin {0, 0} 112b2fc195eSAndrew Gallatin }; 113b2fc195eSAndrew Gallatin 1146d87a65dSAndrew Gallatin static driver_t mxge_driver = 115b2fc195eSAndrew Gallatin { 1166d87a65dSAndrew Gallatin "mxge", 1176d87a65dSAndrew Gallatin mxge_methods, 1186d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 119b2fc195eSAndrew Gallatin }; 120b2fc195eSAndrew Gallatin 1216d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 122b2fc195eSAndrew Gallatin 123b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1246d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1256d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 126b2fc195eSAndrew Gallatin 127b2fc195eSAndrew Gallatin static int 1286d87a65dSAndrew Gallatin mxge_probe(device_t dev) 129b2fc195eSAndrew Gallatin { 1306d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1316d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 132b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 133b2fc195eSAndrew Gallatin return 0; 134b2fc195eSAndrew Gallatin } 135b2fc195eSAndrew Gallatin return ENXIO; 136b2fc195eSAndrew Gallatin } 137b2fc195eSAndrew Gallatin 138b2fc195eSAndrew Gallatin static void 1396d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 140b2fc195eSAndrew Gallatin { 141b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 142b2fc195eSAndrew Gallatin vm_paddr_t pa; 143b2fc195eSAndrew Gallatin vm_offset_t len; 144b2fc195eSAndrew Gallatin int err, action; 145b2fc195eSAndrew Gallatin 146b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 147b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 148b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 149b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 150b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 151b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1526d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 153b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 154b2fc195eSAndrew Gallatin if (err != 0) { 155b2fc195eSAndrew Gallatin device_printf(sc->dev, 156b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 157b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 158b2fc195eSAndrew Gallatin } else { 159b2fc195eSAndrew Gallatin sc->wc = 1; 160b2fc195eSAndrew Gallatin } 161b2fc195eSAndrew Gallatin } 162b2fc195eSAndrew Gallatin 163b2fc195eSAndrew Gallatin 164b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 165b2fc195eSAndrew Gallatin static void 1666d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 167b2fc195eSAndrew Gallatin int error) 168b2fc195eSAndrew Gallatin { 169b2fc195eSAndrew Gallatin if (error == 0) { 170b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 171b2fc195eSAndrew Gallatin } 172b2fc195eSAndrew Gallatin } 173b2fc195eSAndrew Gallatin 174b2fc195eSAndrew Gallatin static int 1756d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 176b2fc195eSAndrew Gallatin bus_size_t alignment) 177b2fc195eSAndrew Gallatin { 178b2fc195eSAndrew Gallatin int err; 179b2fc195eSAndrew Gallatin device_t dev = sc->dev; 180b2fc195eSAndrew Gallatin 181b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 182b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 183b2fc195eSAndrew Gallatin alignment, /* alignment */ 184b2fc195eSAndrew Gallatin 4096, /* boundary */ 185b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 186b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 187b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 188b2fc195eSAndrew Gallatin bytes, /* maxsize */ 189b2fc195eSAndrew Gallatin 1, /* num segs */ 190b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 191b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 192b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 193b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 194b2fc195eSAndrew Gallatin if (err != 0) { 195b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 196b2fc195eSAndrew Gallatin return err; 197b2fc195eSAndrew Gallatin } 198b2fc195eSAndrew Gallatin 199b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 200b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 201b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 202b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 203b2fc195eSAndrew Gallatin if (err != 0) { 204b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 205b2fc195eSAndrew Gallatin goto abort_with_dmat; 206b2fc195eSAndrew Gallatin } 207b2fc195eSAndrew Gallatin 208b2fc195eSAndrew Gallatin /* load the memory */ 209b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2106d87a65dSAndrew Gallatin mxge_dmamap_callback, 211b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 212b2fc195eSAndrew Gallatin if (err != 0) { 213b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 214b2fc195eSAndrew Gallatin goto abort_with_mem; 215b2fc195eSAndrew Gallatin } 216b2fc195eSAndrew Gallatin return 0; 217b2fc195eSAndrew Gallatin 218b2fc195eSAndrew Gallatin abort_with_mem: 219b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 220b2fc195eSAndrew Gallatin abort_with_dmat: 221b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 222b2fc195eSAndrew Gallatin return err; 223b2fc195eSAndrew Gallatin } 224b2fc195eSAndrew Gallatin 225b2fc195eSAndrew Gallatin 226b2fc195eSAndrew Gallatin static void 2276d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 228b2fc195eSAndrew Gallatin { 229b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 230b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 231b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 232b2fc195eSAndrew Gallatin } 233b2fc195eSAndrew Gallatin 234b2fc195eSAndrew Gallatin /* 235b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 236b2fc195eSAndrew Gallatin * SN=x\0 237b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 238b2fc195eSAndrew Gallatin * PC=text\0 239b2fc195eSAndrew Gallatin */ 240b2fc195eSAndrew Gallatin 241b2fc195eSAndrew Gallatin static int 2426d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 243b2fc195eSAndrew Gallatin { 2446d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 245b2fc195eSAndrew Gallatin 246b2fc195eSAndrew Gallatin char *ptr, *limit; 247b2fc195eSAndrew Gallatin int i, found_mac; 248b2fc195eSAndrew Gallatin 249b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2506d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 251b2fc195eSAndrew Gallatin found_mac = 0; 252b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 253b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2545e7d8541SAndrew Gallatin ptr += 1; 255b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 256b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2575e7d8541SAndrew Gallatin ptr += 3; 258b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 259b2fc195eSAndrew Gallatin goto abort; 260b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 261b2fc195eSAndrew Gallatin found_mac = 1; 262b2fc195eSAndrew Gallatin } 2635e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2645e7d8541SAndrew Gallatin ptr += 3; 2655e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2665e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2675e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2685e7d8541SAndrew Gallatin ptr += 3; 2695e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2705e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 271b2fc195eSAndrew Gallatin } 2726d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 273b2fc195eSAndrew Gallatin } 274b2fc195eSAndrew Gallatin 275b2fc195eSAndrew Gallatin if (found_mac) 276b2fc195eSAndrew Gallatin return 0; 277b2fc195eSAndrew Gallatin 278b2fc195eSAndrew Gallatin abort: 279b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 280b2fc195eSAndrew Gallatin 281b2fc195eSAndrew Gallatin return ENXIO; 282b2fc195eSAndrew Gallatin } 283b2fc195eSAndrew Gallatin 284b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 285b2fc195eSAndrew Gallatin static int 2866d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 287b2fc195eSAndrew Gallatin { 288b2fc195eSAndrew Gallatin uint32_t val; 289b2fc195eSAndrew Gallatin unsigned long off; 290b2fc195eSAndrew Gallatin char *va, *cfgptr; 291b2fc195eSAndrew Gallatin uint16_t vendor_id, device_id; 292b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 293b2fc195eSAndrew Gallatin uint32_t *ptr32; 294b2fc195eSAndrew Gallatin 295b2fc195eSAndrew Gallatin /* XXXX 296b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 297b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 298b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 299b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 300b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 301b2fc195eSAndrew Gallatin */ 302b2fc195eSAndrew Gallatin #if 0 303b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 304b2fc195eSAndrew Gallatin config space */ 305b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 306b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 307b2fc195eSAndrew Gallatin val |= 0x40; 308b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 309b2fc195eSAndrew Gallatin return 0; 310b2fc195eSAndrew Gallatin } 311b2fc195eSAndrew Gallatin #endif 312b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 313b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 314b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 315b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 316b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 317b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 318b2fc195eSAndrew Gallatin */ 319b2fc195eSAndrew Gallatin 320b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 321b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 322b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 323b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 324b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 325b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 326b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 327b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 328b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 329b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 330b2fc195eSAndrew Gallatin 331b2fc195eSAndrew Gallatin off = 0xe0000000UL 332b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 333b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 334b2fc195eSAndrew Gallatin + 8 * slot); 335b2fc195eSAndrew Gallatin 336b2fc195eSAndrew Gallatin /* map it into the kernel */ 337b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 338b2fc195eSAndrew Gallatin 339b2fc195eSAndrew Gallatin 340b2fc195eSAndrew Gallatin if (va == NULL) { 341b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 342b2fc195eSAndrew Gallatin return EIO; 343b2fc195eSAndrew Gallatin } 344b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 345b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 346b2fc195eSAndrew Gallatin 347b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 348b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 349b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 350b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 351b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 352b2fc195eSAndrew Gallatin vendor_id, device_id); 353b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 354b2fc195eSAndrew Gallatin return EIO; 355b2fc195eSAndrew Gallatin } 356b2fc195eSAndrew Gallatin 357b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 358b2fc195eSAndrew Gallatin val = *ptr32; 359b2fc195eSAndrew Gallatin 360b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 361b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 362b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 363b2fc195eSAndrew Gallatin return EIO; 364b2fc195eSAndrew Gallatin } 365b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 366b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3675e7d8541SAndrew Gallatin if (mxge_verbose) 368b2fc195eSAndrew Gallatin device_printf(sc->dev, 3695e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 3705e7d8541SAndrew Gallatin "at %d:%d:%d\n", 371b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 372b2fc195eSAndrew Gallatin return 0; 373b2fc195eSAndrew Gallatin } 374b2fc195eSAndrew Gallatin #else 375b2fc195eSAndrew Gallatin static int 3766d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 377b2fc195eSAndrew Gallatin { 378b2fc195eSAndrew Gallatin device_printf(sc->dev, 379b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 380b2fc195eSAndrew Gallatin return ENXIO; 381b2fc195eSAndrew Gallatin } 382b2fc195eSAndrew Gallatin #endif 383b2fc195eSAndrew Gallatin /* 384b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 385b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 386b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 387b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 388b2fc195eSAndrew Gallatin * ECRC generation (if supported). 389b2fc195eSAndrew Gallatin * 390b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 391b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 392b2fc195eSAndrew Gallatin * 393b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 394b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 395b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 396b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 397b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 398b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 399b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 400b2fc195eSAndrew Gallatin */ 401b2fc195eSAndrew Gallatin 402b2fc195eSAndrew Gallatin static void 4036d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 404b2fc195eSAndrew Gallatin { 405b2fc195eSAndrew Gallatin int err, aligned = 0; 406b2fc195eSAndrew Gallatin device_t pdev; 407b2fc195eSAndrew Gallatin uint16_t pvend, pdid; 408b2fc195eSAndrew Gallatin 409d91b1b49SAndrew Gallatin 410d91b1b49SAndrew Gallatin if (mxge_force_firmware != 0) { 411d91b1b49SAndrew Gallatin if (mxge_force_firmware == 1) 412d91b1b49SAndrew Gallatin aligned = 1; 413d91b1b49SAndrew Gallatin else 414d91b1b49SAndrew Gallatin aligned = 0; 415d91b1b49SAndrew Gallatin if (mxge_verbose) 416d91b1b49SAndrew Gallatin device_printf(sc->dev, 417d91b1b49SAndrew Gallatin "Assuming %s completions (forced)\n", 418d91b1b49SAndrew Gallatin aligned ? "aligned" : "unaligned"); 419d91b1b49SAndrew Gallatin goto abort; 420d91b1b49SAndrew Gallatin } 421d91b1b49SAndrew Gallatin 422d91b1b49SAndrew Gallatin /* if the PCIe link width is 4 or less, we can use the aligned 423d91b1b49SAndrew Gallatin firmware and skip any checks */ 424d91b1b49SAndrew Gallatin if (sc->link_width != 0 && sc->link_width <= 4) { 425d91b1b49SAndrew Gallatin device_printf(sc->dev, 426d91b1b49SAndrew Gallatin "PCIe x%d Link, expect reduced performance\n", 427d91b1b49SAndrew Gallatin sc->link_width); 428d91b1b49SAndrew Gallatin aligned = 1; 429d91b1b49SAndrew Gallatin goto abort; 430d91b1b49SAndrew Gallatin } 431d91b1b49SAndrew Gallatin 432b2fc195eSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 433b2fc195eSAndrew Gallatin if (pdev == NULL) { 434b2fc195eSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 435b2fc195eSAndrew Gallatin goto abort; 436b2fc195eSAndrew Gallatin } 437b2fc195eSAndrew Gallatin pvend = pci_read_config(pdev, PCIR_VENDOR, 2); 438b2fc195eSAndrew Gallatin pdid = pci_read_config(pdev, PCIR_DEVICE, 2); 439b2fc195eSAndrew Gallatin 440b2fc195eSAndrew Gallatin /* see if we can enable ECRC's on an upstream 441b2fc195eSAndrew Gallatin Nvidia bridge */ 4426d87a65dSAndrew Gallatin if (mxge_nvidia_ecrc_enable && 443b2fc195eSAndrew Gallatin (pvend == 0x10de && pdid == 0x005d)) { 4446d87a65dSAndrew Gallatin err = mxge_enable_nvidia_ecrc(sc, pdev); 445b2fc195eSAndrew Gallatin if (err == 0) { 446b2fc195eSAndrew Gallatin aligned = 1; 4475e7d8541SAndrew Gallatin if (mxge_verbose) 448b2fc195eSAndrew Gallatin device_printf(sc->dev, 4495e7d8541SAndrew Gallatin "Assuming aligned completions" 4505e7d8541SAndrew Gallatin " (ECRC)\n"); 451b2fc195eSAndrew Gallatin } 452b2fc195eSAndrew Gallatin } 453b2fc195eSAndrew Gallatin /* see if the upstream bridge is known to 454b2fc195eSAndrew Gallatin provided aligned completions */ 455b2fc195eSAndrew Gallatin if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) || 4560fa7f681SAndrew Gallatin /* PLX */ (pvend == 0x10b5 && pdid == 0x8532) || 4570fa7f681SAndrew Gallatin /* Intel */ (pvend == 0x8086 && 458d91b1b49SAndrew Gallatin /* E5000 NorthBridge*/((pdid >= 0x25f7 && pdid <= 0x25fa) || 459d91b1b49SAndrew Gallatin /* E5000 SouthBridge*/ (pdid >= 0x3510 && pdid <= 0x351b)))) { 460d91b1b49SAndrew Gallatin aligned = 1; 4615e7d8541SAndrew Gallatin if (mxge_verbose) 462b2fc195eSAndrew Gallatin device_printf(sc->dev, 4635e7d8541SAndrew Gallatin "Assuming aligned completions " 4645e7d8541SAndrew Gallatin "(0x%x:0x%x)\n", pvend, pdid); 465b2fc195eSAndrew Gallatin } 466b2fc195eSAndrew Gallatin 467b2fc195eSAndrew Gallatin abort: 468b2fc195eSAndrew Gallatin if (aligned) { 4696d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 470b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 471b2fc195eSAndrew Gallatin } else { 4726d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 473b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 474b2fc195eSAndrew Gallatin } 475b2fc195eSAndrew Gallatin } 476b2fc195eSAndrew Gallatin 477b2fc195eSAndrew Gallatin union qualhack 478b2fc195eSAndrew Gallatin { 479b2fc195eSAndrew Gallatin const char *ro_char; 480b2fc195eSAndrew Gallatin char *rw_char; 481b2fc195eSAndrew Gallatin }; 482b2fc195eSAndrew Gallatin 4834da0d523SAndrew Gallatin static int 4844da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 4854da0d523SAndrew Gallatin { 486b824b7d8SAndrew Gallatin 4874da0d523SAndrew Gallatin 4884da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 4894da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 4904da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 4914da0d523SAndrew Gallatin return EIO; 4924da0d523SAndrew Gallatin } 4934da0d523SAndrew Gallatin 4944da0d523SAndrew Gallatin /* save firmware version for sysctl */ 4954da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 4964da0d523SAndrew Gallatin if (mxge_verbose) 4974da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 4984da0d523SAndrew Gallatin 499b824b7d8SAndrew Gallatin sscanf(sc->fw_version, "%d.%d.%d", &sc->fw_ver_major, 500b824b7d8SAndrew Gallatin &sc->fw_ver_minor, &sc->fw_ver_tiny); 5014da0d523SAndrew Gallatin 502b824b7d8SAndrew Gallatin if (!(sc->fw_ver_major == MXGEFW_VERSION_MAJOR 503b824b7d8SAndrew Gallatin && sc->fw_ver_minor == MXGEFW_VERSION_MINOR)) { 5044da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 5054da0d523SAndrew Gallatin sc->fw_version); 5064da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 5074da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 5084da0d523SAndrew Gallatin return EINVAL; 5094da0d523SAndrew Gallatin } 5104da0d523SAndrew Gallatin return 0; 5114da0d523SAndrew Gallatin 5124da0d523SAndrew Gallatin } 513b2fc195eSAndrew Gallatin 514b2fc195eSAndrew Gallatin static int 5156d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 516b2fc195eSAndrew Gallatin { 51733d54970SLuigi Rizzo const struct firmware *fw; 518b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 519b2fc195eSAndrew Gallatin unsigned hdr_offset; 520b2fc195eSAndrew Gallatin const char *fw_data; 521b2fc195eSAndrew Gallatin union qualhack hack; 522b2fc195eSAndrew Gallatin int status; 5234da0d523SAndrew Gallatin unsigned int i; 5244da0d523SAndrew Gallatin char dummy; 525b2fc195eSAndrew Gallatin 526b2fc195eSAndrew Gallatin 527b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 528b2fc195eSAndrew Gallatin 529b2fc195eSAndrew Gallatin if (fw == NULL) { 530b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 531b2fc195eSAndrew Gallatin sc->fw_name); 532b2fc195eSAndrew Gallatin return ENOENT; 533b2fc195eSAndrew Gallatin } 534b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 535b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 536b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 537b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 538b2fc195eSAndrew Gallatin status = ENOSPC; 539b2fc195eSAndrew Gallatin goto abort_with_fw; 540b2fc195eSAndrew Gallatin } 541b2fc195eSAndrew Gallatin *limit = fw->datasize; 542b2fc195eSAndrew Gallatin 543b2fc195eSAndrew Gallatin /* check id */ 544b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 545b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 546b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 547b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 548b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 549b2fc195eSAndrew Gallatin status = EIO; 550b2fc195eSAndrew Gallatin goto abort_with_fw; 551b2fc195eSAndrew Gallatin } 552b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 553b2fc195eSAndrew Gallatin 5544da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 5554da0d523SAndrew Gallatin if (status != 0) 5564da0d523SAndrew Gallatin goto abort_with_fw; 557b2fc195eSAndrew Gallatin 558b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 559b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 5604da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 5614da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 5624da0d523SAndrew Gallatin hack.rw_char + i, 5634da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 5644da0d523SAndrew Gallatin mb(); 5654da0d523SAndrew Gallatin dummy = *sc->sram; 5664da0d523SAndrew Gallatin mb(); 5674da0d523SAndrew Gallatin } 568b2fc195eSAndrew Gallatin 569b2fc195eSAndrew Gallatin status = 0; 570b2fc195eSAndrew Gallatin abort_with_fw: 571b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 572b2fc195eSAndrew Gallatin return status; 573b2fc195eSAndrew Gallatin } 574b2fc195eSAndrew Gallatin 575b2fc195eSAndrew Gallatin /* 576b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 577b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 578b2fc195eSAndrew Gallatin */ 579b2fc195eSAndrew Gallatin 580b2fc195eSAndrew Gallatin static void 5816d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 582b2fc195eSAndrew Gallatin { 583b2fc195eSAndrew Gallatin char buf_bytes[72]; 584b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 585b2fc195eSAndrew Gallatin volatile char *submit; 586b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 587b2fc195eSAndrew Gallatin int i; 588b2fc195eSAndrew Gallatin 589b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 590b2fc195eSAndrew Gallatin 591b2fc195eSAndrew Gallatin /* clear confirmation addr */ 592b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 593b2fc195eSAndrew Gallatin *confirm = 0; 594b2fc195eSAndrew Gallatin mb(); 595b2fc195eSAndrew Gallatin 596b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 597b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 598b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 599b2fc195eSAndrew Gallatin */ 600b2fc195eSAndrew Gallatin 6016d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6026d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 603b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 604b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 605b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 6066d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 6076d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 608b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 609b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 610b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 611b2fc195eSAndrew Gallatin 612b2fc195eSAndrew Gallatin 6130fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_DUMMY_RDMA); 614b2fc195eSAndrew Gallatin 6156d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 616b2fc195eSAndrew Gallatin mb(); 617b2fc195eSAndrew Gallatin DELAY(1000); 618b2fc195eSAndrew Gallatin mb(); 619b2fc195eSAndrew Gallatin i = 0; 620b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 621b2fc195eSAndrew Gallatin DELAY(1000); 622b2fc195eSAndrew Gallatin i++; 623b2fc195eSAndrew Gallatin } 624b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 625b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 626b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 627b2fc195eSAndrew Gallatin *confirm); 628b2fc195eSAndrew Gallatin } 629b2fc195eSAndrew Gallatin return; 630b2fc195eSAndrew Gallatin } 631b2fc195eSAndrew Gallatin 632b2fc195eSAndrew Gallatin static int 6336d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 634b2fc195eSAndrew Gallatin { 635b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 636b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 637b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 6380fa7f681SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_ETH_CMD; 639b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 640b2fc195eSAndrew Gallatin int sleep_total = 0; 641b2fc195eSAndrew Gallatin 642b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 643b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 644b2fc195eSAndrew Gallatin 645b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 646b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 647b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 648b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 6496d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6506d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 651b2fc195eSAndrew Gallatin 652b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 653b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 654a98d6cd7SAndrew Gallatin mtx_lock(&sc->cmd_mtx); 655b2fc195eSAndrew Gallatin response->result = 0xffffffff; 656b2fc195eSAndrew Gallatin mb(); 6576d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 658b2fc195eSAndrew Gallatin 6595e7d8541SAndrew Gallatin /* wait up to 20ms */ 6605e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 661b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 662b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 663b2fc195eSAndrew Gallatin mb(); 664b2fc195eSAndrew Gallatin if (response->result != 0xffffffff) { 665b2fc195eSAndrew Gallatin if (response->result == 0) { 666b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 667a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 668b2fc195eSAndrew Gallatin return 0; 669b2fc195eSAndrew Gallatin } else { 670b2fc195eSAndrew Gallatin device_printf(sc->dev, 6716d87a65dSAndrew Gallatin "mxge: command %d " 672b2fc195eSAndrew Gallatin "failed, result = %d\n", 673b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 674a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 675b2fc195eSAndrew Gallatin return ENXIO; 676b2fc195eSAndrew Gallatin } 677b2fc195eSAndrew Gallatin } 6785e7d8541SAndrew Gallatin DELAY(1000); 679b2fc195eSAndrew Gallatin } 680a98d6cd7SAndrew Gallatin mtx_unlock(&sc->cmd_mtx); 6816d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 682b2fc195eSAndrew Gallatin "result = %d\n", 683b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 684b2fc195eSAndrew Gallatin return EAGAIN; 685b2fc195eSAndrew Gallatin } 686b2fc195eSAndrew Gallatin 6874da0d523SAndrew Gallatin static int 6884da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 6894da0d523SAndrew Gallatin { 6904da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 6914da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 6924da0d523SAndrew Gallatin size_t hdr_offset; 6934da0d523SAndrew Gallatin int status; 6944da0d523SAndrew Gallatin 6954da0d523SAndrew Gallatin /* find running firmware header */ 6964da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 6974da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 6984da0d523SAndrew Gallatin 6994da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 7004da0d523SAndrew Gallatin device_printf(sc->dev, 7014da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 7024da0d523SAndrew Gallatin (int)hdr_offset); 7034da0d523SAndrew Gallatin return EIO; 7044da0d523SAndrew Gallatin } 7054da0d523SAndrew Gallatin 7064da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 7074da0d523SAndrew Gallatin * validate firmware */ 7084da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 7094da0d523SAndrew Gallatin if (hdr == NULL) { 7104da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 7114da0d523SAndrew Gallatin return ENOMEM; 7124da0d523SAndrew Gallatin } 7134da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 7144da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 7154da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 7164da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 7174da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 718b824b7d8SAndrew Gallatin 719b824b7d8SAndrew Gallatin /* 720b824b7d8SAndrew Gallatin * check to see if adopted firmware has bug where adopting 721b824b7d8SAndrew Gallatin * it will cause broadcasts to be filtered unless the NIC 722b824b7d8SAndrew Gallatin * is kept in ALLMULTI mode 723b824b7d8SAndrew Gallatin */ 724b824b7d8SAndrew Gallatin if (sc->fw_ver_major == 1 && sc->fw_ver_minor == 4 && 725b824b7d8SAndrew Gallatin sc->fw_ver_tiny >= 4 && sc->fw_ver_tiny <= 11) { 726b824b7d8SAndrew Gallatin sc->adopted_rx_filter_bug = 1; 727b824b7d8SAndrew Gallatin device_printf(sc->dev, "Adopting fw %d.%d.%d: " 728b824b7d8SAndrew Gallatin "working around rx filter bug\n", 729b824b7d8SAndrew Gallatin sc->fw_ver_major, sc->fw_ver_minor, 730b824b7d8SAndrew Gallatin sc->fw_ver_tiny); 731b824b7d8SAndrew Gallatin } 732b824b7d8SAndrew Gallatin 7334da0d523SAndrew Gallatin return status; 7344da0d523SAndrew Gallatin } 7354da0d523SAndrew Gallatin 736b2fc195eSAndrew Gallatin 737b2fc195eSAndrew Gallatin static int 7386d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 739b2fc195eSAndrew Gallatin { 740b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 741b2fc195eSAndrew Gallatin volatile char *submit; 742b2fc195eSAndrew Gallatin char buf_bytes[72]; 743b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 744b2fc195eSAndrew Gallatin int status, i; 745b2fc195eSAndrew Gallatin 746b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 747b2fc195eSAndrew Gallatin 748b2fc195eSAndrew Gallatin size = sc->sram_size; 7496d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 750b2fc195eSAndrew Gallatin if (status) { 7514da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 7524da0d523SAndrew Gallatin it is new enough */ 7534da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 7544da0d523SAndrew Gallatin if (status) { 7554da0d523SAndrew Gallatin device_printf(sc->dev, 7564da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 757b2fc195eSAndrew Gallatin return status; 758b2fc195eSAndrew Gallatin } 7594da0d523SAndrew Gallatin device_printf(sc->dev, 7604da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 7614da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 7624da0d523SAndrew Gallatin device_printf(sc->dev, 7634da0d523SAndrew Gallatin "Using firmware currently running on NIC" 7644da0d523SAndrew Gallatin ". For optimal\n"); 7654da0d523SAndrew Gallatin device_printf(sc->dev, 7664da0d523SAndrew Gallatin "performance consider loading optimized " 7674da0d523SAndrew Gallatin "firmware\n"); 7684da0d523SAndrew Gallatin } 769d91b1b49SAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 770d91b1b49SAndrew Gallatin sc->tx.boundary = 2048; 771d91b1b49SAndrew Gallatin return 0; 7724da0d523SAndrew Gallatin } 773b2fc195eSAndrew Gallatin /* clear confirmation addr */ 774b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 775b2fc195eSAndrew Gallatin *confirm = 0; 776b2fc195eSAndrew Gallatin mb(); 777b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 778b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 779b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 780b2fc195eSAndrew Gallatin */ 781b2fc195eSAndrew Gallatin 7826d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7836d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 784b2fc195eSAndrew Gallatin 785b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 786b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 787b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 788b2fc195eSAndrew Gallatin 789b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 790b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 791b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 792b2fc195eSAndrew Gallatin */ 793b2fc195eSAndrew Gallatin /* where the code starts*/ 7946d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 795b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 796b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 797b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 798b2fc195eSAndrew Gallatin 7990fa7f681SAndrew Gallatin submit = (volatile char *)(sc->sram + MXGEFW_BOOT_HANDOFF); 8006d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 801b2fc195eSAndrew Gallatin mb(); 802b2fc195eSAndrew Gallatin DELAY(1000); 803b2fc195eSAndrew Gallatin mb(); 804b2fc195eSAndrew Gallatin i = 0; 805b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 806b2fc195eSAndrew Gallatin DELAY(1000*10); 807b2fc195eSAndrew Gallatin i++; 808b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 809b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 810b2fc195eSAndrew Gallatin } 811b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 812b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 813b2fc195eSAndrew Gallatin confirm, *confirm); 814b2fc195eSAndrew Gallatin 815b2fc195eSAndrew Gallatin return ENXIO; 816b2fc195eSAndrew Gallatin } 817b2fc195eSAndrew Gallatin return 0; 818b2fc195eSAndrew Gallatin } 819b2fc195eSAndrew Gallatin 820b2fc195eSAndrew Gallatin static int 8216d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 822b2fc195eSAndrew Gallatin { 8236d87a65dSAndrew Gallatin mxge_cmd_t cmd; 824b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 825b2fc195eSAndrew Gallatin int status; 826b2fc195eSAndrew Gallatin 827b2fc195eSAndrew Gallatin 828b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 829b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 830b2fc195eSAndrew Gallatin 831b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 832b2fc195eSAndrew Gallatin 8335e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 834b2fc195eSAndrew Gallatin return status; 835b2fc195eSAndrew Gallatin } 836b2fc195eSAndrew Gallatin 837b2fc195eSAndrew Gallatin static int 8386d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 839b2fc195eSAndrew Gallatin { 8406d87a65dSAndrew Gallatin mxge_cmd_t cmd; 841b2fc195eSAndrew Gallatin int status; 842b2fc195eSAndrew Gallatin 843b2fc195eSAndrew Gallatin if (pause) 8445e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 845b2fc195eSAndrew Gallatin &cmd); 846b2fc195eSAndrew Gallatin else 8475e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 848b2fc195eSAndrew Gallatin &cmd); 849b2fc195eSAndrew Gallatin 850b2fc195eSAndrew Gallatin if (status) { 851b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 852b2fc195eSAndrew Gallatin return ENXIO; 853b2fc195eSAndrew Gallatin } 854b2fc195eSAndrew Gallatin sc->pause = pause; 855b2fc195eSAndrew Gallatin return 0; 856b2fc195eSAndrew Gallatin } 857b2fc195eSAndrew Gallatin 858b2fc195eSAndrew Gallatin static void 8596d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 860b2fc195eSAndrew Gallatin { 8616d87a65dSAndrew Gallatin mxge_cmd_t cmd; 862b2fc195eSAndrew Gallatin int status; 863b2fc195eSAndrew Gallatin 864b2fc195eSAndrew Gallatin if (promisc) 8655e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 866b2fc195eSAndrew Gallatin &cmd); 867b2fc195eSAndrew Gallatin else 8685e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 869b2fc195eSAndrew Gallatin &cmd); 870b2fc195eSAndrew Gallatin 871b2fc195eSAndrew Gallatin if (status) { 872b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 873b2fc195eSAndrew Gallatin } 874b2fc195eSAndrew Gallatin } 875b2fc195eSAndrew Gallatin 8760fa7f681SAndrew Gallatin static void 8770fa7f681SAndrew Gallatin mxge_set_multicast_list(mxge_softc_t *sc) 8780fa7f681SAndrew Gallatin { 8790fa7f681SAndrew Gallatin mxge_cmd_t cmd; 8800fa7f681SAndrew Gallatin struct ifmultiaddr *ifma; 8810fa7f681SAndrew Gallatin struct ifnet *ifp = sc->ifp; 8820fa7f681SAndrew Gallatin int err; 8830fa7f681SAndrew Gallatin 8840fa7f681SAndrew Gallatin /* This firmware is known to not support multicast */ 8850fa7f681SAndrew Gallatin if (!sc->fw_multicast_support) 8860fa7f681SAndrew Gallatin return; 8870fa7f681SAndrew Gallatin 8880fa7f681SAndrew Gallatin /* Disable multicast filtering while we play with the lists*/ 8890fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_ENABLE_ALLMULTI, &cmd); 8900fa7f681SAndrew Gallatin if (err != 0) { 8910fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_ENABLE_ALLMULTI," 8920fa7f681SAndrew Gallatin " error status: %d\n", err); 8930fa7f681SAndrew Gallatin return; 8940fa7f681SAndrew Gallatin } 8950fa7f681SAndrew Gallatin 896b824b7d8SAndrew Gallatin if (sc->adopted_rx_filter_bug) 897b824b7d8SAndrew Gallatin return; 8980fa7f681SAndrew Gallatin 8990fa7f681SAndrew Gallatin if (ifp->if_flags & IFF_ALLMULTI) 9000fa7f681SAndrew Gallatin /* request to disable multicast filtering, so quit here */ 9010fa7f681SAndrew Gallatin return; 9020fa7f681SAndrew Gallatin 9030fa7f681SAndrew Gallatin /* Flush all the filters */ 9040fa7f681SAndrew Gallatin 9050fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_LEAVE_ALL_MULTICAST_GROUPS, &cmd); 9060fa7f681SAndrew Gallatin if (err != 0) { 9070fa7f681SAndrew Gallatin device_printf(sc->dev, 9080fa7f681SAndrew Gallatin "Failed MXGEFW_LEAVE_ALL_MULTICAST_GROUPS" 9090fa7f681SAndrew Gallatin ", error status: %d\n", err); 9100fa7f681SAndrew Gallatin return; 9110fa7f681SAndrew Gallatin } 9120fa7f681SAndrew Gallatin 9130fa7f681SAndrew Gallatin /* Walk the multicast list, and add each address */ 9140fa7f681SAndrew Gallatin 9150fa7f681SAndrew Gallatin IF_ADDR_LOCK(ifp); 9160fa7f681SAndrew Gallatin TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 9170fa7f681SAndrew Gallatin if (ifma->ifma_addr->sa_family != AF_LINK) 9180fa7f681SAndrew Gallatin continue; 9190fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 9200fa7f681SAndrew Gallatin &cmd.data0, 4); 9210fa7f681SAndrew Gallatin bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr) + 4, 9220fa7f681SAndrew Gallatin &cmd.data1, 2); 9230fa7f681SAndrew Gallatin cmd.data0 = htonl(cmd.data0); 9240fa7f681SAndrew Gallatin cmd.data1 = htonl(cmd.data1); 9250fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_JOIN_MULTICAST_GROUP, &cmd); 9260fa7f681SAndrew Gallatin if (err != 0) { 9270fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed " 9280fa7f681SAndrew Gallatin "MXGEFW_JOIN_MULTICAST_GROUP, error status:" 9290fa7f681SAndrew Gallatin "%d\t", err); 9300fa7f681SAndrew Gallatin /* abort, leaving multicast filtering off */ 9310fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9320fa7f681SAndrew Gallatin return; 9330fa7f681SAndrew Gallatin } 9340fa7f681SAndrew Gallatin } 9350fa7f681SAndrew Gallatin IF_ADDR_UNLOCK(ifp); 9360fa7f681SAndrew Gallatin /* Enable multicast filtering */ 9370fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_DISABLE_ALLMULTI, &cmd); 9380fa7f681SAndrew Gallatin if (err != 0) { 9390fa7f681SAndrew Gallatin device_printf(sc->dev, "Failed MXGEFW_DISABLE_ALLMULTI" 9400fa7f681SAndrew Gallatin ", error status: %d\n", err); 9410fa7f681SAndrew Gallatin } 9420fa7f681SAndrew Gallatin } 9430fa7f681SAndrew Gallatin 9440fa7f681SAndrew Gallatin 945b2fc195eSAndrew Gallatin static int 9466d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc) 947b2fc195eSAndrew Gallatin { 948b2fc195eSAndrew Gallatin 9496d87a65dSAndrew Gallatin mxge_cmd_t cmd; 9505e7d8541SAndrew Gallatin size_t bytes; 9515e7d8541SAndrew Gallatin int status; 952b2fc195eSAndrew Gallatin 953b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 954b2fc195eSAndrew Gallatin is alive */ 955b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 9565e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 957b2fc195eSAndrew Gallatin if (status != 0) { 958b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 959b2fc195eSAndrew Gallatin return ENXIO; 960b2fc195eSAndrew Gallatin } 961b2fc195eSAndrew Gallatin 962091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 963091feecdSAndrew Gallatin 964b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 9655e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\ 9665e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 9675e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 9685e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 9695e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 9705e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 9715e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 972b2fc195eSAndrew Gallatin 9736d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 9745e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 9755e7d8541SAndrew Gallatin 9765e7d8541SAndrew Gallatin 9775e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 9785e7d8541SAndrew Gallatin 9795e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 9805e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 9815e7d8541SAndrew Gallatin 9825e7d8541SAndrew Gallatin 9835e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 9846d87a65dSAndrew Gallatin &cmd); 9855e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 986b2fc195eSAndrew Gallatin if (status != 0) { 987b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 988b2fc195eSAndrew Gallatin return status; 989b2fc195eSAndrew Gallatin } 990b2fc195eSAndrew Gallatin 9915e7d8541SAndrew Gallatin 9925e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 9935e7d8541SAndrew Gallatin 9945e7d8541SAndrew Gallatin 9955e7d8541SAndrew Gallatin /* run a DMA benchmark */ 9965e7d8541SAndrew Gallatin sc->read_dma = sc->write_dma = sc->read_write_dma = 0; 9975e7d8541SAndrew Gallatin 9985e7d8541SAndrew Gallatin /* Read DMA */ 999a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 1000a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10015e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10000; 10025e7d8541SAndrew Gallatin 10035e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10045e7d8541SAndrew Gallatin if (status != 0) 10055e7d8541SAndrew Gallatin device_printf(sc->dev, "read dma benchmark failed\n"); 10065e7d8541SAndrew Gallatin else 10075e7d8541SAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 10085e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10095e7d8541SAndrew Gallatin 10105e7d8541SAndrew Gallatin /* Write DMA */ 1011a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 1012a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10135e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x1; 10145e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10155e7d8541SAndrew Gallatin if (status != 0) 10165e7d8541SAndrew Gallatin device_printf(sc->dev, "write dma benchmark failed\n"); 10175e7d8541SAndrew Gallatin else 10185e7d8541SAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 10195e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10205e7d8541SAndrew Gallatin /* Read/Write DMA */ 1021a98d6cd7SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->dmabench_dma.bus_addr); 1022a98d6cd7SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->dmabench_dma.bus_addr); 10235e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10001; 10245e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 10255e7d8541SAndrew Gallatin if (status != 0) 10265e7d8541SAndrew Gallatin device_printf(sc->dev, "read/write dma benchmark failed\n"); 10275e7d8541SAndrew Gallatin else 10285e7d8541SAndrew Gallatin sc->read_write_dma = 10295e7d8541SAndrew Gallatin ((cmd.data0>>16) * sc->tx.boundary * 2 * 2) / 10305e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 10315e7d8541SAndrew Gallatin 1032b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 10335e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 10345e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 10355e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 1036b2fc195eSAndrew Gallatin sc->tx.req = 0; 1037b2fc195eSAndrew Gallatin sc->tx.done = 0; 10385e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 1039a82c2581SAndrew Gallatin sc->tx.wake = 0; 1040a82c2581SAndrew Gallatin sc->tx.stall = 0; 1041b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 1042b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 1043b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 1044a98d6cd7SAndrew Gallatin sc->fw_stats->valid = 0; 1045a98d6cd7SAndrew Gallatin sc->fw_stats->send_done_count = 0; 10466d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 10476d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 10486d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 10490fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 1050b2fc195eSAndrew Gallatin return status; 1051b2fc195eSAndrew Gallatin } 1052b2fc195eSAndrew Gallatin 1053b2fc195eSAndrew Gallatin static int 10546d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 1055b2fc195eSAndrew Gallatin { 10566d87a65dSAndrew Gallatin mxge_softc_t *sc; 1057b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 1058b2fc195eSAndrew Gallatin int err; 1059b2fc195eSAndrew Gallatin 1060b2fc195eSAndrew Gallatin sc = arg1; 1061b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 1062b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 1063b2fc195eSAndrew Gallatin if (err != 0) { 1064b2fc195eSAndrew Gallatin return err; 1065b2fc195eSAndrew Gallatin } 1066b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 1067b2fc195eSAndrew Gallatin return 0; 1068b2fc195eSAndrew Gallatin 1069b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 1070b2fc195eSAndrew Gallatin return EINVAL; 1071b2fc195eSAndrew Gallatin 1072a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 10735e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 1074b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 10755e7d8541SAndrew Gallatin 1076a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1077b2fc195eSAndrew Gallatin return err; 1078b2fc195eSAndrew Gallatin } 1079b2fc195eSAndrew Gallatin 1080b2fc195eSAndrew Gallatin static int 10816d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 1082b2fc195eSAndrew Gallatin { 10836d87a65dSAndrew Gallatin mxge_softc_t *sc; 1084b2fc195eSAndrew Gallatin unsigned int enabled; 1085b2fc195eSAndrew Gallatin int err; 1086b2fc195eSAndrew Gallatin 1087b2fc195eSAndrew Gallatin sc = arg1; 1088b2fc195eSAndrew Gallatin enabled = sc->pause; 1089b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 1090b2fc195eSAndrew Gallatin if (err != 0) { 1091b2fc195eSAndrew Gallatin return err; 1092b2fc195eSAndrew Gallatin } 1093b2fc195eSAndrew Gallatin if (enabled == sc->pause) 1094b2fc195eSAndrew Gallatin return 0; 1095b2fc195eSAndrew Gallatin 1096a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 10976d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 1098a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 1099b2fc195eSAndrew Gallatin return err; 1100b2fc195eSAndrew Gallatin } 1101b2fc195eSAndrew Gallatin 1102b2fc195eSAndrew Gallatin static int 11036d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 1104b2fc195eSAndrew Gallatin { 1105b2fc195eSAndrew Gallatin int err; 1106b2fc195eSAndrew Gallatin 1107b2fc195eSAndrew Gallatin if (arg1 == NULL) 1108b2fc195eSAndrew Gallatin return EFAULT; 1109b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 1110b2fc195eSAndrew Gallatin arg1 = NULL; 1111b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 1112b2fc195eSAndrew Gallatin 1113b2fc195eSAndrew Gallatin return err; 1114b2fc195eSAndrew Gallatin } 1115b2fc195eSAndrew Gallatin 1116b2fc195eSAndrew Gallatin static void 11176d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1118b2fc195eSAndrew Gallatin { 1119b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1120b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 11215e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1122b2fc195eSAndrew Gallatin 1123b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1124b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1125b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1126b2fc195eSAndrew Gallatin 11275e7d8541SAndrew Gallatin /* random information */ 11285e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11295e7d8541SAndrew Gallatin "firmware_version", 11305e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 11315e7d8541SAndrew Gallatin 0, "firmware version"); 11325e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11335e7d8541SAndrew Gallatin "serial_number", 11345e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 11355e7d8541SAndrew Gallatin 0, "serial number"); 11365e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 11375e7d8541SAndrew Gallatin "product_code", 11385e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 11395e7d8541SAndrew Gallatin 0, "product_code"); 11405e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1141d91b1b49SAndrew Gallatin "pcie_link_width", 1142d91b1b49SAndrew Gallatin CTLFLAG_RD, &sc->link_width, 1143d91b1b49SAndrew Gallatin 0, "tx_boundary"); 1144d91b1b49SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11455e7d8541SAndrew Gallatin "tx_boundary", 11465e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 11475e7d8541SAndrew Gallatin 0, "tx_boundary"); 11485e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1149091feecdSAndrew Gallatin "write_combine", 1150091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1151091feecdSAndrew Gallatin 0, "write combining PIO?"); 1152091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11535e7d8541SAndrew Gallatin "read_dma_MBs", 11545e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 11555e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 11565e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11575e7d8541SAndrew Gallatin "write_dma_MBs", 11585e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 11595e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 11605e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11615e7d8541SAndrew Gallatin "read_write_dma_MBs", 11625e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 11635e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 11645e7d8541SAndrew Gallatin 11655e7d8541SAndrew Gallatin 11665e7d8541SAndrew Gallatin /* performance related tunables */ 1167b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1168b2fc195eSAndrew Gallatin "intr_coal_delay", 1169b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11706d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1171b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1172b2fc195eSAndrew Gallatin 1173b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1174b2fc195eSAndrew Gallatin "flow_control_enabled", 1175b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 11766d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1177b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1178b2fc195eSAndrew Gallatin 1179b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11805e7d8541SAndrew Gallatin "deassert_wait", 11815e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 11825e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1183b2fc195eSAndrew Gallatin 1184b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1185b2fc195eSAndrew Gallatin Need to swap it */ 1186b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1187b2fc195eSAndrew Gallatin "link_up", 1188b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 11896d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1190b2fc195eSAndrew Gallatin "I", "link up"); 1191b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1192b2fc195eSAndrew Gallatin "rdma_tags_available", 1193b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 11946d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1195b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1196b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1197b2fc195eSAndrew Gallatin "dropped_link_overflow", 1198b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 11996d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1200b2fc195eSAndrew Gallatin "I", "dropped_link_overflow"); 1201b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1202b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1203b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1204b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 12056d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1206b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1207b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 12080fa7f681SAndrew Gallatin "dropped_multicast_filtered", 12090fa7f681SAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 12100fa7f681SAndrew Gallatin &fw->dropped_multicast_filtered, 12110fa7f681SAndrew Gallatin 0, mxge_handle_be32, 12120fa7f681SAndrew Gallatin "I", "dropped_multicast_filtered"); 12130fa7f681SAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1214b2fc195eSAndrew Gallatin "dropped_runt", 1215b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 12166d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1217b2fc195eSAndrew Gallatin "I", "dropped_runt"); 1218b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1219b2fc195eSAndrew Gallatin "dropped_overrun", 1220b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 12216d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1222b2fc195eSAndrew Gallatin "I", "dropped_overrun"); 1223b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1224b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1225b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1226b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 12276d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1228b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1229b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1230b2fc195eSAndrew Gallatin "dropped_no_big_buffer", 1231b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 12326d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1233b2fc195eSAndrew Gallatin "I", "dropped_no_big_buffer"); 1234b2fc195eSAndrew Gallatin 1235b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1236b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12375e7d8541SAndrew Gallatin "rx_small_cnt", 12385e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 12395e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12405e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12415e7d8541SAndrew Gallatin "rx_big_cnt", 12425e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 12435e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 12445e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1245b2fc195eSAndrew Gallatin "tx_req", 1246b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1247b2fc195eSAndrew Gallatin 0, "tx_req"); 1248b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1249b2fc195eSAndrew Gallatin "tx_done", 1250b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1251b2fc195eSAndrew Gallatin 0, "tx_done"); 1252b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12535e7d8541SAndrew Gallatin "tx_pkt_done", 12545e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 12555e7d8541SAndrew Gallatin 0, "tx_done"); 1256a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1257a82c2581SAndrew Gallatin "tx_stall", 1258a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.stall, 1259a82c2581SAndrew Gallatin 0, "tx_stall"); 1260a82c2581SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1261a82c2581SAndrew Gallatin "tx_wake", 1262a82c2581SAndrew Gallatin CTLFLAG_RD, &sc->tx.wake, 1263a82c2581SAndrew Gallatin 0, "tx_wake"); 12645e7d8541SAndrew Gallatin 12655e7d8541SAndrew Gallatin /* verbose printing? */ 1266b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 12675e7d8541SAndrew Gallatin "verbose", 12685e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 12695e7d8541SAndrew Gallatin 0, "verbose printing"); 1270b2fc195eSAndrew Gallatin 1271b2fc195eSAndrew Gallatin } 1272b2fc195eSAndrew Gallatin 1273b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1274b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1275b2fc195eSAndrew Gallatin 1276b2fc195eSAndrew Gallatin static inline void 12776d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1278b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1279b2fc195eSAndrew Gallatin { 1280b2fc195eSAndrew Gallatin int idx, starting_slot; 1281b2fc195eSAndrew Gallatin starting_slot = tx->req; 1282b2fc195eSAndrew Gallatin while (cnt > 1) { 1283b2fc195eSAndrew Gallatin cnt--; 1284b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 12856d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1286b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1287b2fc195eSAndrew Gallatin mb(); 1288b2fc195eSAndrew Gallatin } 1289b2fc195eSAndrew Gallatin } 1290b2fc195eSAndrew Gallatin 1291b2fc195eSAndrew Gallatin /* 1292b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1293b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1294b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1295b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1296b2fc195eSAndrew Gallatin */ 1297b2fc195eSAndrew Gallatin 1298b2fc195eSAndrew Gallatin static inline void 12996d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1300b2fc195eSAndrew Gallatin int cnt) 1301b2fc195eSAndrew Gallatin { 1302b2fc195eSAndrew Gallatin int idx, i; 1303b2fc195eSAndrew Gallatin uint32_t *src_ints; 1304b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1305b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1306b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 13075e7d8541SAndrew Gallatin uint8_t last_flags; 1308b2fc195eSAndrew Gallatin 1309b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1310b2fc195eSAndrew Gallatin 13115e7d8541SAndrew Gallatin last_flags = src->flags; 13125e7d8541SAndrew Gallatin src->flags = 0; 1313b2fc195eSAndrew Gallatin mb(); 1314b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1315b2fc195eSAndrew Gallatin srcp = src; 1316b2fc195eSAndrew Gallatin 1317b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1318b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 13196d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1320b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1321b2fc195eSAndrew Gallatin srcp += 2; 1322b2fc195eSAndrew Gallatin dstp += 2; 1323b2fc195eSAndrew Gallatin } 1324b2fc195eSAndrew Gallatin } else { 1325b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1326b2fc195eSAndrew Gallatin that it is submitted below */ 13276d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1328b2fc195eSAndrew Gallatin i = 0; 1329b2fc195eSAndrew Gallatin } 1330b2fc195eSAndrew Gallatin if (i < cnt) { 1331b2fc195eSAndrew Gallatin /* submit the first request */ 13326d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1333b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1334b2fc195eSAndrew Gallatin } 1335b2fc195eSAndrew Gallatin 1336b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 13375e7d8541SAndrew Gallatin src->flags = last_flags; 1338b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1339b2fc195eSAndrew Gallatin src_ints+=3; 1340b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1341b2fc195eSAndrew Gallatin dst_ints+=3; 1342b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1343b2fc195eSAndrew Gallatin tx->req += cnt; 1344b2fc195eSAndrew Gallatin mb(); 1345b2fc195eSAndrew Gallatin } 1346b2fc195eSAndrew Gallatin 1347b2fc195eSAndrew Gallatin static inline void 13486d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt) 1349b2fc195eSAndrew Gallatin { 1350b2fc195eSAndrew Gallatin tx->req += cnt; 1351b2fc195eSAndrew Gallatin mb(); 1352b2fc195eSAndrew Gallatin while (cnt >= 4) { 13536d87a65dSAndrew Gallatin mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64); 1354b2fc195eSAndrew Gallatin mb(); 1355b2fc195eSAndrew Gallatin src += 4; 1356b2fc195eSAndrew Gallatin cnt -= 4; 1357b2fc195eSAndrew Gallatin } 1358b2fc195eSAndrew Gallatin if (cnt > 0) { 1359b2fc195eSAndrew Gallatin /* pad it to 64 bytes. The src is 64 bytes bigger than it 1360b2fc195eSAndrew Gallatin needs to be so that we don't overrun it */ 13610fa7f681SAndrew Gallatin mxge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), src, 64); 1362b2fc195eSAndrew Gallatin mb(); 1363b2fc195eSAndrew Gallatin } 1364b2fc195eSAndrew Gallatin } 1365b2fc195eSAndrew Gallatin 1366b2fc195eSAndrew Gallatin static void 1367aed8e389SAndrew Gallatin mxge_encap_tso(mxge_softc_t *sc, struct mbuf *m, int busdma_seg_cnt) 1368aed8e389SAndrew Gallatin { 1369aed8e389SAndrew Gallatin mxge_tx_buf_t *tx; 1370aed8e389SAndrew Gallatin mcp_kreq_ether_send_t *req; 1371aed8e389SAndrew Gallatin bus_dma_segment_t *seg; 1372aed8e389SAndrew Gallatin struct ether_header *eh; 1373aed8e389SAndrew Gallatin struct ip *ip; 1374aed8e389SAndrew Gallatin struct tcphdr *tcp; 1375aed8e389SAndrew Gallatin uint32_t low, high_swapped; 1376aed8e389SAndrew Gallatin int len, seglen, cum_len, cum_len_next; 1377aed8e389SAndrew Gallatin int next_is_first, chop, cnt, rdma_count, small; 1378aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset, cksum_offset, mss; 1379aed8e389SAndrew Gallatin uint8_t flags, flags_next; 1380aed8e389SAndrew Gallatin static int once; 1381aed8e389SAndrew Gallatin 1382aed8e389SAndrew Gallatin mss = m->m_pkthdr.tso_segsz; 1383aed8e389SAndrew Gallatin 1384aed8e389SAndrew Gallatin /* negative cum_len signifies to the 1385aed8e389SAndrew Gallatin * send loop that we are still in the 1386aed8e389SAndrew Gallatin * header portion of the TSO packet. 1387aed8e389SAndrew Gallatin */ 1388aed8e389SAndrew Gallatin 1389aed8e389SAndrew Gallatin /* ensure we have the ethernet, IP and TCP 1390aed8e389SAndrew Gallatin header together in the first mbuf, copy 1391aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1392aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1393aed8e389SAndrew Gallatin + sizeof (*ip))) { 1394aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1395aed8e389SAndrew Gallatin sc->scratch); 1396aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1397aed8e389SAndrew Gallatin } else { 1398aed8e389SAndrew Gallatin eh = mtod(m, struct ether_header *); 1399aed8e389SAndrew Gallatin } 1400aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1401aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) + (ip->ip_hl << 2) 1402aed8e389SAndrew Gallatin + sizeof (*tcp))) { 1403aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + (ip->ip_hl << 2) 1404aed8e389SAndrew Gallatin + sizeof (*tcp), sc->scratch); 1405aed8e389SAndrew Gallatin eh = (struct ether_header *) sc->scratch; 1406aed8e389SAndrew Gallatin ip = (struct ip *) (eh + 1); 1407aed8e389SAndrew Gallatin } 1408aed8e389SAndrew Gallatin 1409aed8e389SAndrew Gallatin tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); 1410aed8e389SAndrew Gallatin cum_len = -(sizeof (*eh) + ((ip->ip_hl + tcp->th_off) << 2)); 1411aed8e389SAndrew Gallatin 1412aed8e389SAndrew Gallatin /* TSO implies checksum offload on this hardware */ 1413aed8e389SAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1414aed8e389SAndrew Gallatin flags = MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST; 1415aed8e389SAndrew Gallatin 1416aed8e389SAndrew Gallatin 1417aed8e389SAndrew Gallatin /* for TSO, pseudo_hdr_offset holds mss. 1418aed8e389SAndrew Gallatin * The firmware figures out where to put 1419aed8e389SAndrew Gallatin * the checksum by parsing the header. */ 1420aed8e389SAndrew Gallatin pseudo_hdr_offset = htobe16(mss); 1421aed8e389SAndrew Gallatin 1422aed8e389SAndrew Gallatin tx = &sc->tx; 1423aed8e389SAndrew Gallatin req = tx->req_list; 1424aed8e389SAndrew Gallatin seg = tx->seg_list; 1425aed8e389SAndrew Gallatin cnt = 0; 1426aed8e389SAndrew Gallatin rdma_count = 0; 1427aed8e389SAndrew Gallatin /* "rdma_count" is the number of RDMAs belonging to the 1428aed8e389SAndrew Gallatin * current packet BEFORE the current send request. For 1429aed8e389SAndrew Gallatin * non-TSO packets, this is equal to "count". 1430aed8e389SAndrew Gallatin * For TSO packets, rdma_count needs to be reset 1431aed8e389SAndrew Gallatin * to 0 after a segment cut. 1432aed8e389SAndrew Gallatin * 1433aed8e389SAndrew Gallatin * The rdma_count field of the send request is 1434aed8e389SAndrew Gallatin * the number of RDMAs of the packet starting at 1435aed8e389SAndrew Gallatin * that request. For TSO send requests with one ore more cuts 1436aed8e389SAndrew Gallatin * in the middle, this is the number of RDMAs starting 1437aed8e389SAndrew Gallatin * after the last cut in the request. All previous 1438aed8e389SAndrew Gallatin * segments before the last cut implicitly have 1 RDMA. 1439aed8e389SAndrew Gallatin * 1440aed8e389SAndrew Gallatin * Since the number of RDMAs is not known beforehand, 1441aed8e389SAndrew Gallatin * it must be filled-in retroactively - after each 1442aed8e389SAndrew Gallatin * segmentation cut or at the end of the entire packet. 1443aed8e389SAndrew Gallatin */ 1444aed8e389SAndrew Gallatin 1445aed8e389SAndrew Gallatin while (busdma_seg_cnt) { 1446aed8e389SAndrew Gallatin /* Break the busdma segment up into pieces*/ 1447aed8e389SAndrew Gallatin low = MXGE_LOWPART_TO_U32(seg->ds_addr); 1448aed8e389SAndrew Gallatin high_swapped = htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1449aed8e389SAndrew Gallatin len = seglen = seg->ds_len; 1450aed8e389SAndrew Gallatin 1451aed8e389SAndrew Gallatin while (len) { 1452aed8e389SAndrew Gallatin flags_next = flags & ~MXGEFW_FLAGS_FIRST; 1453aed8e389SAndrew Gallatin cum_len_next = cum_len + seglen; 1454aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count + 1; 1455aed8e389SAndrew Gallatin if (__predict_true(cum_len >= 0)) { 1456aed8e389SAndrew Gallatin /* payload */ 1457aed8e389SAndrew Gallatin chop = (cum_len_next > mss); 1458aed8e389SAndrew Gallatin cum_len_next = cum_len_next % mss; 1459aed8e389SAndrew Gallatin next_is_first = (cum_len_next == 0); 1460aed8e389SAndrew Gallatin flags |= chop * MXGEFW_FLAGS_TSO_CHOP; 1461aed8e389SAndrew Gallatin flags_next |= next_is_first * 1462aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST; 1463aed8e389SAndrew Gallatin rdma_count |= -(chop | next_is_first); 1464aed8e389SAndrew Gallatin rdma_count += chop & !next_is_first; 1465aed8e389SAndrew Gallatin } else if (cum_len_next >= 0) { 1466aed8e389SAndrew Gallatin /* header ends */ 1467aed8e389SAndrew Gallatin rdma_count = -1; 1468aed8e389SAndrew Gallatin cum_len_next = 0; 1469aed8e389SAndrew Gallatin seglen = -cum_len; 1470aed8e389SAndrew Gallatin small = (mss <= MXGEFW_SEND_SMALL_SIZE); 1471aed8e389SAndrew Gallatin flags_next = MXGEFW_FLAGS_TSO_PLD | 1472aed8e389SAndrew Gallatin MXGEFW_FLAGS_FIRST | 1473aed8e389SAndrew Gallatin (small * MXGEFW_FLAGS_SMALL); 1474aed8e389SAndrew Gallatin } 1475aed8e389SAndrew Gallatin 1476aed8e389SAndrew Gallatin req->addr_high = high_swapped; 1477aed8e389SAndrew Gallatin req->addr_low = htobe32(low); 1478aed8e389SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 1479aed8e389SAndrew Gallatin req->pad = 0; 1480aed8e389SAndrew Gallatin req->rdma_count = 1; 1481aed8e389SAndrew Gallatin req->length = htobe16(seglen); 1482aed8e389SAndrew Gallatin req->cksum_offset = cksum_offset; 1483aed8e389SAndrew Gallatin req->flags = flags | ((cum_len & 1) * 1484aed8e389SAndrew Gallatin MXGEFW_FLAGS_ALIGN_ODD); 1485aed8e389SAndrew Gallatin low += seglen; 1486aed8e389SAndrew Gallatin len -= seglen; 1487aed8e389SAndrew Gallatin cum_len = cum_len_next; 1488aed8e389SAndrew Gallatin flags = flags_next; 1489aed8e389SAndrew Gallatin req++; 1490aed8e389SAndrew Gallatin cnt++; 1491aed8e389SAndrew Gallatin rdma_count++; 1492aed8e389SAndrew Gallatin if (__predict_false(cksum_offset > seglen)) 1493aed8e389SAndrew Gallatin cksum_offset -= seglen; 1494aed8e389SAndrew Gallatin else 1495aed8e389SAndrew Gallatin cksum_offset = 0; 1496aed8e389SAndrew Gallatin if (__predict_false(cnt > MXGE_MAX_SEND_DESC)) 1497aed8e389SAndrew Gallatin goto drop; 1498aed8e389SAndrew Gallatin } 1499aed8e389SAndrew Gallatin busdma_seg_cnt--; 1500aed8e389SAndrew Gallatin seg++; 1501aed8e389SAndrew Gallatin } 1502aed8e389SAndrew Gallatin (req-rdma_count)->rdma_count = rdma_count; 1503aed8e389SAndrew Gallatin 1504aed8e389SAndrew Gallatin do { 1505aed8e389SAndrew Gallatin req--; 1506aed8e389SAndrew Gallatin req->flags |= MXGEFW_FLAGS_TSO_LAST; 1507aed8e389SAndrew Gallatin } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); 1508aed8e389SAndrew Gallatin 1509aed8e389SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1510aed8e389SAndrew Gallatin if (tx->wc_fifo == NULL) 1511aed8e389SAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1512aed8e389SAndrew Gallatin else 1513aed8e389SAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1514aed8e389SAndrew Gallatin return; 1515aed8e389SAndrew Gallatin 1516aed8e389SAndrew Gallatin drop: 1517aed8e389SAndrew Gallatin m_freem(m); 1518aed8e389SAndrew Gallatin sc->ifp->if_oerrors++; 1519aed8e389SAndrew Gallatin if (!once) { 1520aed8e389SAndrew Gallatin printf("MXGE_MAX_SEND_DESC exceeded via TSO!\n"); 1521aed8e389SAndrew Gallatin printf("mss = %d, %ld!\n", mss, (long)seg - (long)tx->seg_list); 1522aed8e389SAndrew Gallatin once = 1; 1523aed8e389SAndrew Gallatin } 1524aed8e389SAndrew Gallatin return; 1525aed8e389SAndrew Gallatin 1526aed8e389SAndrew Gallatin } 1527aed8e389SAndrew Gallatin 1528aed8e389SAndrew Gallatin static void 15296d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1530b2fc195eSAndrew Gallatin { 1531b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 1532b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1533b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1534b2fc195eSAndrew Gallatin struct ifnet *ifp; 15356d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1536b2fc195eSAndrew Gallatin struct ether_header *eh; 1537b2fc195eSAndrew Gallatin struct ip *ip; 1538aed8e389SAndrew Gallatin int cnt, cum_len, err, i, idx, odd_flag; 1539aed8e389SAndrew Gallatin uint16_t pseudo_hdr_offset; 1540aed8e389SAndrew Gallatin uint8_t flags, cksum_offset; 1541b2fc195eSAndrew Gallatin 1542b2fc195eSAndrew Gallatin 1543b2fc195eSAndrew Gallatin 1544b2fc195eSAndrew Gallatin ifp = sc->ifp; 1545b2fc195eSAndrew Gallatin tx = &sc->tx; 1546b2fc195eSAndrew Gallatin 1547b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1548b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1549b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1550aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1551b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1552b2fc195eSAndrew Gallatin if (err == EFBIG) { 1553b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1554b2fc195eSAndrew Gallatin to defrag */ 1555b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1556b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1557b2fc195eSAndrew Gallatin goto drop; 1558b2fc195eSAndrew Gallatin } 1559b2fc195eSAndrew Gallatin m = m_tmp; 1560b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1561b2fc195eSAndrew Gallatin tx->info[idx].map, 1562aed8e389SAndrew Gallatin m, tx->seg_list, &cnt, 1563b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1564b2fc195eSAndrew Gallatin } 1565b2fc195eSAndrew Gallatin if (err != 0) { 1566aed8e389SAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d" 1567aed8e389SAndrew Gallatin " packet len = %d\n", err, m->m_pkthdr.len); 1568b2fc195eSAndrew Gallatin goto drop; 1569b2fc195eSAndrew Gallatin } 1570b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1571b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 15725e7d8541SAndrew Gallatin tx->info[idx].m = m; 1573b2fc195eSAndrew Gallatin 1574aed8e389SAndrew Gallatin 1575aed8e389SAndrew Gallatin /* TSO is different enough, we handle it in another routine */ 1576aed8e389SAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_TSO)) { 1577aed8e389SAndrew Gallatin mxge_encap_tso(sc, m, cnt); 1578aed8e389SAndrew Gallatin return; 1579aed8e389SAndrew Gallatin } 1580aed8e389SAndrew Gallatin 1581b2fc195eSAndrew Gallatin req = tx->req_list; 1582b2fc195eSAndrew Gallatin cksum_offset = 0; 15835e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 15845e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1585b2fc195eSAndrew Gallatin 1586b2fc195eSAndrew Gallatin /* checksum offloading? */ 1587b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1588aed8e389SAndrew Gallatin /* ensure ip header is in first mbuf, copy 1589aed8e389SAndrew Gallatin it to a scratch buffer if not */ 1590aed8e389SAndrew Gallatin if (__predict_false(m->m_len < sizeof (*eh) 1591aed8e389SAndrew Gallatin + sizeof (*ip))) { 1592aed8e389SAndrew Gallatin m_copydata(m, 0, sizeof (*eh) + sizeof (*ip), 1593aed8e389SAndrew Gallatin sc->scratch); 1594aed8e389SAndrew Gallatin eh = (struct ether_header *)sc->scratch; 1595aed8e389SAndrew Gallatin } else { 1596b2fc195eSAndrew Gallatin eh = mtod(m, struct ether_header *); 1597aed8e389SAndrew Gallatin } 1598b2fc195eSAndrew Gallatin ip = (struct ip *) (eh + 1); 1599b2fc195eSAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1600b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 16015e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1602b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 16035e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1604aed8e389SAndrew Gallatin odd_flag = MXGEFW_FLAGS_ALIGN_ODD; 1605aed8e389SAndrew Gallatin } else { 1606aed8e389SAndrew Gallatin odd_flag = 0; 1607b2fc195eSAndrew Gallatin } 16085e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 16095e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1610b2fc195eSAndrew Gallatin 1611b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1612b2fc195eSAndrew Gallatin cum_len = 0; 1613aed8e389SAndrew Gallatin seg = tx->seg_list; 16145e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1615b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1616b2fc195eSAndrew Gallatin req->addr_low = 16176d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1618b2fc195eSAndrew Gallatin req->addr_high = 16196d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1620b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1621b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1622b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1623b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1624b2fc195eSAndrew Gallatin else 1625b2fc195eSAndrew Gallatin cksum_offset = 0; 16265e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16275e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16285e7d8541SAndrew Gallatin req->rdma_count = 1; 1629aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1630b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1631b2fc195eSAndrew Gallatin seg++; 1632b2fc195eSAndrew Gallatin req++; 1633b2fc195eSAndrew Gallatin req->flags = 0; 1634b2fc195eSAndrew Gallatin } 1635b2fc195eSAndrew Gallatin req--; 1636b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1637b2fc195eSAndrew Gallatin if (cum_len < 60) { 1638b2fc195eSAndrew Gallatin req++; 1639b2fc195eSAndrew Gallatin req->addr_low = 16406d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1641b2fc195eSAndrew Gallatin req->addr_high = 16426d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1643b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 16445e7d8541SAndrew Gallatin req->cksum_offset = 0; 16455e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 16465e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 16475e7d8541SAndrew Gallatin req->rdma_count = 1; 1648aed8e389SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * odd_flag); 1649b2fc195eSAndrew Gallatin cnt++; 1650b2fc195eSAndrew Gallatin } 16515e7d8541SAndrew Gallatin 16525e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 16535e7d8541SAndrew Gallatin #if 0 16545e7d8541SAndrew Gallatin /* print what the firmware will see */ 16555e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 16565e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 16575e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 16585e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 16595e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 16605e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 16615e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 16625e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 16635e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 16645e7d8541SAndrew Gallatin } 16655e7d8541SAndrew Gallatin printf("--------------\n"); 16665e7d8541SAndrew Gallatin #endif 16675e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1668b2fc195eSAndrew Gallatin if (tx->wc_fifo == NULL) 16696d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1670b2fc195eSAndrew Gallatin else 16716d87a65dSAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1672b2fc195eSAndrew Gallatin return; 1673b2fc195eSAndrew Gallatin 1674b2fc195eSAndrew Gallatin drop: 1675b2fc195eSAndrew Gallatin m_freem(m); 1676b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1677b2fc195eSAndrew Gallatin return; 1678b2fc195eSAndrew Gallatin } 1679b2fc195eSAndrew Gallatin 1680b2fc195eSAndrew Gallatin 16816d914a32SAndrew Gallatin 16826d914a32SAndrew Gallatin 16836d914a32SAndrew Gallatin static inline void 16846d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1685b2fc195eSAndrew Gallatin { 1686b2fc195eSAndrew Gallatin struct mbuf *m; 1687b2fc195eSAndrew Gallatin struct ifnet *ifp; 1688b2fc195eSAndrew Gallatin 1689b2fc195eSAndrew Gallatin ifp = sc->ifp; 16906d914a32SAndrew Gallatin while ((sc->tx.mask - (sc->tx.req - sc->tx.done)) 16916d914a32SAndrew Gallatin > MXGE_MAX_SEND_DESC) { 1692b2fc195eSAndrew Gallatin 16936d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 16946d914a32SAndrew Gallatin if (m == NULL) { 16956d914a32SAndrew Gallatin return; 16966d914a32SAndrew Gallatin } 1697b2fc195eSAndrew Gallatin /* let BPF see it */ 1698b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1699b2fc195eSAndrew Gallatin 1700b2fc195eSAndrew Gallatin /* give it to the nic */ 17016d87a65dSAndrew Gallatin mxge_encap(sc, m); 17026d914a32SAndrew Gallatin } 17036d914a32SAndrew Gallatin /* ran out of transmit slots */ 1704a82c2581SAndrew Gallatin if ((sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 1705b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1706a82c2581SAndrew Gallatin sc->tx.stall++; 1707a82c2581SAndrew Gallatin } 1708b2fc195eSAndrew Gallatin } 1709b2fc195eSAndrew Gallatin 1710b2fc195eSAndrew Gallatin static void 17116d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1712b2fc195eSAndrew Gallatin { 17136d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1714b2fc195eSAndrew Gallatin 1715b2fc195eSAndrew Gallatin 1716a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 17176d87a65dSAndrew Gallatin mxge_start_locked(sc); 1718a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 1719b2fc195eSAndrew Gallatin } 1720b2fc195eSAndrew Gallatin 17215e7d8541SAndrew Gallatin /* 17225e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 17235e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 17245e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 17255e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 17265e7d8541SAndrew Gallatin * in a burst 17275e7d8541SAndrew Gallatin */ 17285e7d8541SAndrew Gallatin static inline void 17295e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 17305e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 17315e7d8541SAndrew Gallatin { 17325e7d8541SAndrew Gallatin uint32_t low; 17335e7d8541SAndrew Gallatin 17345e7d8541SAndrew Gallatin low = src->addr_low; 17355e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 1736a1480dfbSAndrew Gallatin mxge_pio_copy(dst, src, 4 * sizeof (*src)); 1737a1480dfbSAndrew Gallatin mb(); 1738a1480dfbSAndrew Gallatin mxge_pio_copy(dst + 4, src + 4, 4 * sizeof (*src)); 17395e7d8541SAndrew Gallatin mb(); 174040385a5fSAndrew Gallatin src->addr_low = low; 17415e7d8541SAndrew Gallatin dst->addr_low = low; 17425e7d8541SAndrew Gallatin mb(); 17435e7d8541SAndrew Gallatin } 17445e7d8541SAndrew Gallatin 1745b2fc195eSAndrew Gallatin static int 17466d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1747b2fc195eSAndrew Gallatin { 1748b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1749b2fc195eSAndrew Gallatin struct mbuf *m; 17506d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1751b2fc195eSAndrew Gallatin int cnt, err; 1752b2fc195eSAndrew Gallatin 1753b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1754b2fc195eSAndrew Gallatin if (m == NULL) { 1755b2fc195eSAndrew Gallatin rx->alloc_fail++; 1756b2fc195eSAndrew Gallatin err = ENOBUFS; 1757b2fc195eSAndrew Gallatin goto done; 1758b2fc195eSAndrew Gallatin } 1759b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1760b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1761b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1762b2fc195eSAndrew Gallatin if (err != 0) { 1763b2fc195eSAndrew Gallatin m_free(m); 1764b2fc195eSAndrew Gallatin goto done; 1765b2fc195eSAndrew Gallatin } 1766b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1767b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 17686d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1769b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 17706d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1771b2fc195eSAndrew Gallatin 1772b2fc195eSAndrew Gallatin done: 1773b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 17745e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 17755e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 17765e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 17775e7d8541SAndrew Gallatin else { 1778b2fc195eSAndrew Gallatin mb(); 17795e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 17805e7d8541SAndrew Gallatin } 1781b2fc195eSAndrew Gallatin } 1782b2fc195eSAndrew Gallatin return err; 1783b2fc195eSAndrew Gallatin } 1784b2fc195eSAndrew Gallatin 1785b2fc195eSAndrew Gallatin static int 17866d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1787b2fc195eSAndrew Gallatin { 1788b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1789b2fc195eSAndrew Gallatin struct mbuf *m; 17906d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1791b2fc195eSAndrew Gallatin int cnt, err; 1792b2fc195eSAndrew Gallatin 1793b2fc195eSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes); 1794b2fc195eSAndrew Gallatin if (m == NULL) { 1795b2fc195eSAndrew Gallatin rx->alloc_fail++; 1796b2fc195eSAndrew Gallatin err = ENOBUFS; 1797b2fc195eSAndrew Gallatin goto done; 1798b2fc195eSAndrew Gallatin } 1799b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1800b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1801b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1802b2fc195eSAndrew Gallatin if (err != 0) { 1803b2fc195eSAndrew Gallatin m_free(m); 1804b2fc195eSAndrew Gallatin goto done; 1805b2fc195eSAndrew Gallatin } 1806b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1807b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 18086d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1809b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 18106d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1811b2fc195eSAndrew Gallatin 1812b2fc195eSAndrew Gallatin done: 1813b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 18145e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 18155e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 18165e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 18175e7d8541SAndrew Gallatin else { 1818b2fc195eSAndrew Gallatin mb(); 18195e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 18205e7d8541SAndrew Gallatin } 1821b2fc195eSAndrew Gallatin } 1822b2fc195eSAndrew Gallatin return err; 1823b2fc195eSAndrew Gallatin } 1824b2fc195eSAndrew Gallatin 1825b2fc195eSAndrew Gallatin static inline void 18265e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 18275e7d8541SAndrew Gallatin { 18285e7d8541SAndrew Gallatin struct ether_header *eh; 18295e7d8541SAndrew Gallatin struct ip *ip; 18305e7d8541SAndrew Gallatin 18315e7d8541SAndrew Gallatin eh = mtod(m, struct ether_header *); 18325e7d8541SAndrew Gallatin if (__predict_true(eh->ether_type == htons(ETHERTYPE_IP))) { 18335e7d8541SAndrew Gallatin ip = (struct ip *)(eh + 1); 18345e7d8541SAndrew Gallatin if (__predict_true(ip->ip_p == IPPROTO_TCP || 18355e7d8541SAndrew Gallatin ip->ip_p == IPPROTO_UDP)) { 18365e7d8541SAndrew Gallatin m->m_pkthdr.csum_data = csum; 18375e7d8541SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_DATA_VALID; 18385e7d8541SAndrew Gallatin } 18395e7d8541SAndrew Gallatin } 18405e7d8541SAndrew Gallatin } 18415e7d8541SAndrew Gallatin 18425e7d8541SAndrew Gallatin static inline void 18435e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum) 1844b2fc195eSAndrew Gallatin { 1845b2fc195eSAndrew Gallatin struct ifnet *ifp; 1846b2fc195eSAndrew Gallatin struct mbuf *m = 0; /* -Wunitialized */ 1847b2fc195eSAndrew Gallatin struct mbuf *m_prev = 0; /* -Wunitialized */ 1848b2fc195eSAndrew Gallatin struct mbuf *m_head = 0; 1849b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 18506d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1851b2fc195eSAndrew Gallatin int idx; 1852b2fc195eSAndrew Gallatin 1853b2fc195eSAndrew Gallatin 1854b2fc195eSAndrew Gallatin rx = &sc->rx_big; 1855b2fc195eSAndrew Gallatin ifp = sc->ifp; 1856b2fc195eSAndrew Gallatin while (len > 0) { 1857b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1858b2fc195eSAndrew Gallatin rx->cnt++; 1859b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1860b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1861b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 18626d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 1863b2fc195eSAndrew Gallatin goto drop; 1864b2fc195eSAndrew Gallatin } 1865b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1866b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1867b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1868b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1869b2fc195eSAndrew Gallatin 1870b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1871b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1872b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1873b2fc195eSAndrew Gallatin 1874b2fc195eSAndrew Gallatin /* chain multiple segments together */ 1875b2fc195eSAndrew Gallatin if (!m_head) { 1876b2fc195eSAndrew Gallatin m_head = m; 1877b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st bytes so that 1878b2fc195eSAndrew Gallatin * packet is properly aligned */ 18795e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1880b2fc195eSAndrew Gallatin m->m_pkthdr.len = len; 18815e7d8541SAndrew Gallatin m->m_len = sc->big_bytes - MXGEFW_PAD; 1882b2fc195eSAndrew Gallatin } else { 1883b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1884b2fc195eSAndrew Gallatin m->m_flags &= ~M_PKTHDR; 1885b2fc195eSAndrew Gallatin m_prev->m_next = m; 1886b2fc195eSAndrew Gallatin } 1887b2fc195eSAndrew Gallatin len -= m->m_len; 1888b2fc195eSAndrew Gallatin m_prev = m; 1889b2fc195eSAndrew Gallatin } 1890b2fc195eSAndrew Gallatin 1891b2fc195eSAndrew Gallatin /* trim trailing garbage from the last mbuf in the chain. If 1892b2fc195eSAndrew Gallatin * there is any garbage, len will be negative */ 1893b2fc195eSAndrew Gallatin m->m_len += len; 1894b2fc195eSAndrew Gallatin 1895b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 18965e7d8541SAndrew Gallatin if (sc->csum_flag) 18975e7d8541SAndrew Gallatin mxge_rx_csum(m_head, csum); 1898b2fc195eSAndrew Gallatin 1899b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1900b2fc195eSAndrew Gallatin m_head->m_pkthdr.rcvif = ifp; 1901b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1902b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m_head); 1903b2fc195eSAndrew Gallatin return; 1904b2fc195eSAndrew Gallatin 1905b2fc195eSAndrew Gallatin drop: 1906b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf(s) are re-cycled by running 1907b2fc195eSAndrew Gallatin every slot through the allocator */ 1908b2fc195eSAndrew Gallatin if (m_head) { 1909b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1910b2fc195eSAndrew Gallatin m_freem(m_head); 1911b2fc195eSAndrew Gallatin } else { 19125e7d8541SAndrew Gallatin len -= (sc->big_bytes + MXGEFW_PAD); 1913b2fc195eSAndrew Gallatin } 1914b2fc195eSAndrew Gallatin while ((int)len > 0) { 1915b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1916b2fc195eSAndrew Gallatin rx->cnt++; 1917b2fc195eSAndrew Gallatin m = rx->info[idx].m; 19186d87a65dSAndrew Gallatin if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) { 1919b2fc195eSAndrew Gallatin m_freem(m); 1920b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1921b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1922b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, 1923b2fc195eSAndrew Gallatin BUS_DMASYNC_POSTREAD); 1924b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1925b2fc195eSAndrew Gallatin 1926b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1927b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1928b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1929b2fc195eSAndrew Gallatin } 1930b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1931b2fc195eSAndrew Gallatin } 1932b2fc195eSAndrew Gallatin 1933b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1934b2fc195eSAndrew Gallatin 1935b2fc195eSAndrew Gallatin } 1936b2fc195eSAndrew Gallatin 1937b2fc195eSAndrew Gallatin static inline void 19385e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 1939b2fc195eSAndrew Gallatin { 1940b2fc195eSAndrew Gallatin struct ifnet *ifp; 1941b2fc195eSAndrew Gallatin struct mbuf *m; 19426d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1943b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 1944b2fc195eSAndrew Gallatin int idx; 1945b2fc195eSAndrew Gallatin 1946b2fc195eSAndrew Gallatin ifp = sc->ifp; 1947b2fc195eSAndrew Gallatin rx = &sc->rx_small; 1948b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1949b2fc195eSAndrew Gallatin rx->cnt++; 1950b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1951b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1952b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 19536d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 1954b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 1955b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1956b2fc195eSAndrew Gallatin return; 1957b2fc195eSAndrew Gallatin } 1958b2fc195eSAndrew Gallatin 1959b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1960b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1961b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1962b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1963b2fc195eSAndrew Gallatin 1964b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1965b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1966b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1967b2fc195eSAndrew Gallatin 1968b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 1969b2fc195eSAndrew Gallatin * aligned */ 19705e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1971b2fc195eSAndrew Gallatin 1972b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 19735e7d8541SAndrew Gallatin if (sc->csum_flag) 19745e7d8541SAndrew Gallatin mxge_rx_csum(m, csum); 1975b2fc195eSAndrew Gallatin 1976b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1977b2fc195eSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 1978b2fc195eSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 1979b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1980b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 1981b2fc195eSAndrew Gallatin } 1982b2fc195eSAndrew Gallatin 1983b2fc195eSAndrew Gallatin static inline void 19845e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 19855e7d8541SAndrew Gallatin { 19865e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 19875e7d8541SAndrew Gallatin int limit = 0; 19885e7d8541SAndrew Gallatin uint16_t length; 19895e7d8541SAndrew Gallatin uint16_t checksum; 19905e7d8541SAndrew Gallatin 19915e7d8541SAndrew Gallatin 19925e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 19935e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 19945e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 19955e7d8541SAndrew Gallatin checksum = ntohs(rx_done->entry[rx_done->idx].checksum); 1996b4db9009SAndrew Gallatin if (length <= (MHLEN - MXGEFW_PAD)) 19975e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 19985e7d8541SAndrew Gallatin else 19995e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 20005e7d8541SAndrew Gallatin rx_done->cnt++; 20015e7d8541SAndrew Gallatin rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1); 20025e7d8541SAndrew Gallatin 20035e7d8541SAndrew Gallatin /* limit potential for livelock */ 20045e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * mxge_max_intr_slots)) 20055e7d8541SAndrew Gallatin break; 20065e7d8541SAndrew Gallatin 20075e7d8541SAndrew Gallatin } 20085e7d8541SAndrew Gallatin } 20095e7d8541SAndrew Gallatin 20105e7d8541SAndrew Gallatin 20115e7d8541SAndrew Gallatin static inline void 20126d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 2013b2fc195eSAndrew Gallatin { 2014b2fc195eSAndrew Gallatin struct ifnet *ifp; 20156d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 2016b2fc195eSAndrew Gallatin struct mbuf *m; 2017b2fc195eSAndrew Gallatin bus_dmamap_t map; 20185e7d8541SAndrew Gallatin int idx, limit; 2019b2fc195eSAndrew Gallatin 20205e7d8541SAndrew Gallatin limit = 0; 2021b2fc195eSAndrew Gallatin tx = &sc->tx; 2022b2fc195eSAndrew Gallatin ifp = sc->ifp; 20235e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 2024b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 2025b2fc195eSAndrew Gallatin tx->done++; 2026b2fc195eSAndrew Gallatin m = tx->info[idx].m; 2027b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 2028b2fc195eSAndrew Gallatin segment per-mbuf */ 2029b2fc195eSAndrew Gallatin if (m != NULL) { 2030b2fc195eSAndrew Gallatin ifp->if_opackets++; 2031b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 2032b2fc195eSAndrew Gallatin map = tx->info[idx].map; 2033b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 2034b2fc195eSAndrew Gallatin m_freem(m); 2035b2fc195eSAndrew Gallatin } 20365e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 20375e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 20385e7d8541SAndrew Gallatin tx->pkt_done++; 20395e7d8541SAndrew Gallatin } 20405e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 20415e7d8541SAndrew Gallatin 2 full tx rings per call */ 20425e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 20435e7d8541SAndrew Gallatin break; 2044b2fc195eSAndrew Gallatin } 2045b2fc195eSAndrew Gallatin 2046b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 2047b2fc195eSAndrew Gallatin its OK to send packets */ 2048b2fc195eSAndrew Gallatin 2049b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 2050b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 2051a98d6cd7SAndrew Gallatin mtx_lock(&sc->tx_mtx); 2052b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2053a82c2581SAndrew Gallatin sc->tx.wake++; 20546d87a65dSAndrew Gallatin mxge_start_locked(sc); 2055a98d6cd7SAndrew Gallatin mtx_unlock(&sc->tx_mtx); 2056b2fc195eSAndrew Gallatin } 2057b2fc195eSAndrew Gallatin } 2058b2fc195eSAndrew Gallatin 2059b2fc195eSAndrew Gallatin static void 20606d87a65dSAndrew Gallatin mxge_intr(void *arg) 2061b2fc195eSAndrew Gallatin { 20626d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 20635e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 20645e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 20655e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 20665e7d8541SAndrew Gallatin uint32_t send_done_count; 20675e7d8541SAndrew Gallatin uint8_t valid; 2068b2fc195eSAndrew Gallatin 2069b2fc195eSAndrew Gallatin 20705e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 20715e7d8541SAndrew Gallatin if (!stats->valid) { 20725e7d8541SAndrew Gallatin return; 2073b2fc195eSAndrew Gallatin } 20745e7d8541SAndrew Gallatin valid = stats->valid; 2075b2fc195eSAndrew Gallatin 2076dc8731d4SAndrew Gallatin if (!sc->msi_enabled) { 20775e7d8541SAndrew Gallatin /* lower legacy IRQ */ 20785e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 20795e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 20805e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 20815e7d8541SAndrew Gallatin stats->valid = 0; 2082dc8731d4SAndrew Gallatin } else { 2083dc8731d4SAndrew Gallatin stats->valid = 0; 2084dc8731d4SAndrew Gallatin } 2085dc8731d4SAndrew Gallatin 2086dc8731d4SAndrew Gallatin /* loop while waiting for legacy irq deassertion */ 20875e7d8541SAndrew Gallatin do { 20885e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 20895e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 20905e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 20915e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 20925e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 20935e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 20945e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 2095b2fc195eSAndrew Gallatin } 20965e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 2097b2fc195eSAndrew Gallatin 20985e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 20995e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 21005e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 2101b2fc195eSAndrew Gallatin if (sc->link_state) { 21025e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 21035e7d8541SAndrew Gallatin if (mxge_verbose) 21045e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 2105b2fc195eSAndrew Gallatin } else { 21065e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 21075e7d8541SAndrew Gallatin if (mxge_verbose) 21085e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 2109b2fc195eSAndrew Gallatin } 2110b2fc195eSAndrew Gallatin } 2111b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 2112b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 2113b2fc195eSAndrew Gallatin sc->rdma_tags_available = 2114b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 21155e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 21165e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 21175e7d8541SAndrew Gallatin } 21185e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 2119b2fc195eSAndrew Gallatin } 2120b2fc195eSAndrew Gallatin 21215e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 21225e7d8541SAndrew Gallatin if (valid & 0x1) 21235e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 21245e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 2125b2fc195eSAndrew Gallatin } 2126b2fc195eSAndrew Gallatin 2127b2fc195eSAndrew Gallatin static void 21286d87a65dSAndrew Gallatin mxge_init(void *arg) 2129b2fc195eSAndrew Gallatin { 2130b2fc195eSAndrew Gallatin } 2131b2fc195eSAndrew Gallatin 2132b2fc195eSAndrew Gallatin 2133b2fc195eSAndrew Gallatin 2134b2fc195eSAndrew Gallatin static void 21356d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 2136b2fc195eSAndrew Gallatin { 2137b2fc195eSAndrew Gallatin int i; 2138b2fc195eSAndrew Gallatin 2139b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2140b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2141b2fc195eSAndrew Gallatin continue; 2142b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2143b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2144b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2145b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2146b2fc195eSAndrew Gallatin } 2147b2fc195eSAndrew Gallatin 2148b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2149b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 2150b2fc195eSAndrew Gallatin continue; 2151b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 2152b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2153b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 2154b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 2155b2fc195eSAndrew Gallatin } 2156b2fc195eSAndrew Gallatin 2157b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2158dce01b9bSAndrew Gallatin sc->tx.info[i].flag = 0; 2159b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 2160b2fc195eSAndrew Gallatin continue; 2161b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 2162b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2163b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 2164b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 2165b2fc195eSAndrew Gallatin } 2166b2fc195eSAndrew Gallatin } 2167b2fc195eSAndrew Gallatin 2168b2fc195eSAndrew Gallatin static void 21696d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 2170b2fc195eSAndrew Gallatin { 2171b2fc195eSAndrew Gallatin int i; 2172b2fc195eSAndrew Gallatin 2173aed8e389SAndrew Gallatin if (sc->tx.req_bytes != NULL) 2174b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 2175aed8e389SAndrew Gallatin if (sc->tx.seg_list != NULL) 2176aed8e389SAndrew Gallatin free(sc->tx.seg_list, M_DEVBUF); 2177b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 2178b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 2179b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 2180b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 2181b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 2182c2657176SAndrew Gallatin if (sc->tx.dmat != NULL) { 2183b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2184b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 2185b2fc195eSAndrew Gallatin sc->tx.info[i].map); 2186b2fc195eSAndrew Gallatin } 2187c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 2188c2657176SAndrew Gallatin } 2189b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 2190b2fc195eSAndrew Gallatin } 2191b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 2192c2657176SAndrew Gallatin if (sc->rx_small.dmat != NULL) { 2193b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2194b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2195b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 2196b2fc195eSAndrew Gallatin } 2197c2657176SAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 2198c2657176SAndrew Gallatin sc->rx_small.extra_map); 2199c2657176SAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 2200c2657176SAndrew Gallatin } 2201b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 2202b2fc195eSAndrew Gallatin } 2203b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 2204c2657176SAndrew Gallatin if (sc->rx_big.dmat != NULL) { 2205b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2206b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2207b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 2208b2fc195eSAndrew Gallatin } 2209b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 2210b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 2211b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 2212b2fc195eSAndrew Gallatin } 2213c2657176SAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 2214c2657176SAndrew Gallatin } 2215c2657176SAndrew Gallatin } 2216b2fc195eSAndrew Gallatin 2217b2fc195eSAndrew Gallatin static int 22186d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 2219b2fc195eSAndrew Gallatin { 22206d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2221b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 2222b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 2223b2fc195eSAndrew Gallatin int i, err; 2224b2fc195eSAndrew Gallatin unsigned long bytes; 2225b2fc195eSAndrew Gallatin 2226b2fc195eSAndrew Gallatin /* get ring sizes */ 22275e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 2228b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 22295e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 2230b2fc195eSAndrew Gallatin if (err != 0) { 2231b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 2232b2fc195eSAndrew Gallatin goto abort_with_nothing; 2233b2fc195eSAndrew Gallatin } 2234b2fc195eSAndrew Gallatin 2235b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 2236b2fc195eSAndrew Gallatin 2237b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 2238b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 223976bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 2240a82c2581SAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 224176bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 2242b2fc195eSAndrew Gallatin 2243b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 2244b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 2245b2fc195eSAndrew Gallatin 2246b2fc195eSAndrew Gallatin err = ENOMEM; 2247b2fc195eSAndrew Gallatin 2248b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 2249b2fc195eSAndrew Gallatin bytes = 8 + 22505e7d8541SAndrew Gallatin sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4); 2251b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 2252b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 2253b2fc195eSAndrew Gallatin goto abort_with_nothing; 2254b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 2255b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 2256b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 2257b2fc195eSAndrew Gallatin 2258aed8e389SAndrew Gallatin /* allocate the tx busdma segment list */ 2259aed8e389SAndrew Gallatin bytes = sizeof (*sc->tx.seg_list) * MXGE_MAX_SEND_DESC; 2260aed8e389SAndrew Gallatin sc->tx.seg_list = (bus_dma_segment_t *) 2261aed8e389SAndrew Gallatin malloc(bytes, M_DEVBUF, M_WAITOK); 2262aed8e389SAndrew Gallatin if (sc->tx.seg_list == NULL) 2263aed8e389SAndrew Gallatin goto abort_with_alloc; 2264aed8e389SAndrew Gallatin 2265b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 2266b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 2267b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2268b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 2269b2fc195eSAndrew Gallatin goto abort_with_alloc; 2270b2fc195eSAndrew Gallatin 2271b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 2272b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2273b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 2274b2fc195eSAndrew Gallatin goto abort_with_alloc; 2275b2fc195eSAndrew Gallatin 2276b2fc195eSAndrew Gallatin /* allocate the host info rings */ 2277b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 2278b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2279b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 2280b2fc195eSAndrew Gallatin goto abort_with_alloc; 2281b2fc195eSAndrew Gallatin 2282b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 2283b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2284b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 2285b2fc195eSAndrew Gallatin goto abort_with_alloc; 2286b2fc195eSAndrew Gallatin 2287b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 2288b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 2289b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 2290b2fc195eSAndrew Gallatin goto abort_with_alloc; 2291b2fc195eSAndrew Gallatin 2292b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 2293b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2294b2fc195eSAndrew Gallatin 1, /* alignment */ 2295b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 2296b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2297b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2298b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2299aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 2300aed8e389SAndrew Gallatin MXGE_MAX_SEND_DESC/2, /* num segs */ 2301b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 2302b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2303b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2304b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 2305b2fc195eSAndrew Gallatin 2306b2fc195eSAndrew Gallatin if (err != 0) { 2307b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 2308b2fc195eSAndrew Gallatin err); 2309b2fc195eSAndrew Gallatin goto abort_with_alloc; 2310b2fc195eSAndrew Gallatin } 2311b2fc195eSAndrew Gallatin 2312b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2313b2fc195eSAndrew Gallatin 1, /* alignment */ 2314b2fc195eSAndrew Gallatin 4096, /* boundary */ 2315b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2316b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2317b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2318b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 2319b2fc195eSAndrew Gallatin 1, /* num segs */ 2320b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 2321b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2322b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2323b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 2324b2fc195eSAndrew Gallatin if (err != 0) { 2325b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 2326b2fc195eSAndrew Gallatin err); 2327b2fc195eSAndrew Gallatin goto abort_with_alloc; 2328b2fc195eSAndrew Gallatin } 2329b2fc195eSAndrew Gallatin 2330b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2331b2fc195eSAndrew Gallatin 1, /* alignment */ 2332b2fc195eSAndrew Gallatin 4096, /* boundary */ 2333b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2334b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2335b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2336b2fc195eSAndrew Gallatin 4096, /* maxsize */ 2337b2fc195eSAndrew Gallatin 1, /* num segs */ 2338b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2339b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2340b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2341b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2342b2fc195eSAndrew Gallatin if (err != 0) { 2343b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2344b2fc195eSAndrew Gallatin err); 2345b2fc195eSAndrew Gallatin goto abort_with_alloc; 2346b2fc195eSAndrew Gallatin } 2347b2fc195eSAndrew Gallatin 2348b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2349b2fc195eSAndrew Gallatin in each ring */ 2350b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2351b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2352b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2353b2fc195eSAndrew Gallatin if (err != 0) { 2354b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2355b2fc195eSAndrew Gallatin err); 2356b2fc195eSAndrew Gallatin goto abort_with_alloc; 2357b2fc195eSAndrew Gallatin } 2358b2fc195eSAndrew Gallatin } 2359b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2360b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2361b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2362b2fc195eSAndrew Gallatin if (err != 0) { 2363b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2364b2fc195eSAndrew Gallatin err); 2365b2fc195eSAndrew Gallatin goto abort_with_alloc; 2366b2fc195eSAndrew Gallatin } 2367b2fc195eSAndrew Gallatin } 2368b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2369b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2370b2fc195eSAndrew Gallatin if (err != 0) { 2371b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2372b2fc195eSAndrew Gallatin err); 2373b2fc195eSAndrew Gallatin goto abort_with_alloc; 2374b2fc195eSAndrew Gallatin } 2375b2fc195eSAndrew Gallatin 2376b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2377b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2378b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2379b2fc195eSAndrew Gallatin if (err != 0) { 2380b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2381b2fc195eSAndrew Gallatin err); 2382b2fc195eSAndrew Gallatin goto abort_with_alloc; 2383b2fc195eSAndrew Gallatin } 2384b2fc195eSAndrew Gallatin } 2385b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2386b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2387b2fc195eSAndrew Gallatin if (err != 0) { 2388b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2389b2fc195eSAndrew Gallatin err); 2390b2fc195eSAndrew Gallatin goto abort_with_alloc; 2391b2fc195eSAndrew Gallatin } 2392b2fc195eSAndrew Gallatin return 0; 2393b2fc195eSAndrew Gallatin 2394b2fc195eSAndrew Gallatin abort_with_alloc: 23956d87a65dSAndrew Gallatin mxge_free_rings(sc); 2396b2fc195eSAndrew Gallatin 2397b2fc195eSAndrew Gallatin abort_with_nothing: 2398b2fc195eSAndrew Gallatin return err; 2399b2fc195eSAndrew Gallatin } 2400b2fc195eSAndrew Gallatin 2401b2fc195eSAndrew Gallatin static int 24026d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2403b2fc195eSAndrew Gallatin { 24046d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2405b2fc195eSAndrew Gallatin int i, err; 2406b2fc195eSAndrew Gallatin bus_dmamap_t map; 24070fa7f681SAndrew Gallatin bus_addr_t bus; 2408b2fc195eSAndrew Gallatin 2409b2fc195eSAndrew Gallatin 24107d542e2dSAndrew Gallatin /* Copy the MAC address in case it was overridden */ 24117d542e2dSAndrew Gallatin bcopy(IF_LLADDR(sc->ifp), sc->mac_addr, ETHER_ADDR_LEN); 24127d542e2dSAndrew Gallatin 24136d87a65dSAndrew Gallatin err = mxge_reset(sc); 2414b2fc195eSAndrew Gallatin if (err != 0) { 2415b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2416b2fc195eSAndrew Gallatin return EIO; 2417b2fc195eSAndrew Gallatin } 2418a98d6cd7SAndrew Gallatin bzero(sc->rx_done.entry, 2419a98d6cd7SAndrew Gallatin mxge_max_intr_slots * sizeof(*sc->rx_done.entry)); 2420b2fc195eSAndrew Gallatin 2421b2fc195eSAndrew Gallatin if (MCLBYTES >= 24225e7d8541SAndrew Gallatin sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD) 2423b2fc195eSAndrew Gallatin sc->big_bytes = MCLBYTES; 2424b2fc195eSAndrew Gallatin else 2425b2fc195eSAndrew Gallatin sc->big_bytes = MJUMPAGESIZE; 2426b2fc195eSAndrew Gallatin 2427b2fc195eSAndrew Gallatin 2428b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2429b2fc195eSAndrew Gallatin 24305e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2431b2fc195eSAndrew Gallatin sc->tx.lanai = 2432b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 24336d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 24345e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2435b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2436b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 24375e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2438b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2439b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2440b2fc195eSAndrew Gallatin 2441b2fc195eSAndrew Gallatin if (err != 0) { 2442b2fc195eSAndrew Gallatin device_printf(sc->dev, 2443b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2444a98d6cd7SAndrew Gallatin return EIO; 2445b2fc195eSAndrew Gallatin } 2446b2fc195eSAndrew Gallatin 2447b2fc195eSAndrew Gallatin if (sc->wc) { 24480fa7f681SAndrew Gallatin sc->tx.wc_fifo = sc->sram + MXGEFW_ETH_SEND_4; 24490fa7f681SAndrew Gallatin sc->rx_small.wc_fifo = sc->sram + MXGEFW_ETH_RECV_SMALL; 24500fa7f681SAndrew Gallatin sc->rx_big.wc_fifo = sc->sram + MXGEFW_ETH_RECV_BIG; 2451b2fc195eSAndrew Gallatin } else { 2452b2fc195eSAndrew Gallatin sc->tx.wc_fifo = 0; 2453b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = 0; 2454b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = 0; 2455b2fc195eSAndrew Gallatin } 2456b2fc195eSAndrew Gallatin 2457b2fc195eSAndrew Gallatin 2458b2fc195eSAndrew Gallatin /* stock receive rings */ 2459b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2460b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 24616d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2462b2fc195eSAndrew Gallatin if (err) { 2463b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2464b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2465b2fc195eSAndrew Gallatin goto abort; 2466b2fc195eSAndrew Gallatin } 2467b2fc195eSAndrew Gallatin } 2468b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2469b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 24706d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2471b2fc195eSAndrew Gallatin if (err) { 2472b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2473b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2474b2fc195eSAndrew Gallatin goto abort; 2475b2fc195eSAndrew Gallatin } 2476b2fc195eSAndrew Gallatin } 2477b2fc195eSAndrew Gallatin 2478b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2479b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2480b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2481b2fc195eSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN; 24825e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2483b4db9009SAndrew Gallatin cmd.data0 = MHLEN - MXGEFW_PAD; 24845e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2485b2fc195eSAndrew Gallatin &cmd); 2486b2fc195eSAndrew Gallatin cmd.data0 = sc->big_bytes; 24875e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 24880fa7f681SAndrew Gallatin 24890fa7f681SAndrew Gallatin if (err != 0) { 24900fa7f681SAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 24910fa7f681SAndrew Gallatin goto abort; 24920fa7f681SAndrew Gallatin } 24930fa7f681SAndrew Gallatin 2494b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 24956d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 24966d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 24970fa7f681SAndrew Gallatin cmd.data2 = sizeof(struct mcp_irq_data); 24980fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd); 24990fa7f681SAndrew Gallatin 25000fa7f681SAndrew Gallatin if (err != 0) { 25010fa7f681SAndrew Gallatin bus = sc->fw_stats_dma.bus_addr; 25020fa7f681SAndrew Gallatin bus += offsetof(struct mcp_irq_data, send_done_count); 25030fa7f681SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(bus); 25040fa7f681SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(bus); 25050fa7f681SAndrew Gallatin err = mxge_send_cmd(sc, 25060fa7f681SAndrew Gallatin MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, 25070fa7f681SAndrew Gallatin &cmd); 25080fa7f681SAndrew Gallatin /* Firmware cannot support multicast without STATS_DMA_V2 */ 25090fa7f681SAndrew Gallatin sc->fw_multicast_support = 0; 25100fa7f681SAndrew Gallatin } else { 25110fa7f681SAndrew Gallatin sc->fw_multicast_support = 1; 25120fa7f681SAndrew Gallatin } 2513b2fc195eSAndrew Gallatin 2514b2fc195eSAndrew Gallatin if (err != 0) { 2515b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2516b2fc195eSAndrew Gallatin goto abort; 2517b2fc195eSAndrew Gallatin } 2518b2fc195eSAndrew Gallatin 2519b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 25205e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2521b2fc195eSAndrew Gallatin if (err) { 2522b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2523b2fc195eSAndrew Gallatin goto abort; 2524b2fc195eSAndrew Gallatin } 2525b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2526b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2527b2fc195eSAndrew Gallatin 2528b2fc195eSAndrew Gallatin return 0; 2529b2fc195eSAndrew Gallatin 2530b2fc195eSAndrew Gallatin 2531b2fc195eSAndrew Gallatin abort: 25326d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2533a98d6cd7SAndrew Gallatin 2534b2fc195eSAndrew Gallatin return err; 2535b2fc195eSAndrew Gallatin } 2536b2fc195eSAndrew Gallatin 2537b2fc195eSAndrew Gallatin static int 25386d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2539b2fc195eSAndrew Gallatin { 25406d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2541b2fc195eSAndrew Gallatin int err, old_down_cnt; 2542b2fc195eSAndrew Gallatin 2543b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2544b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2545b2fc195eSAndrew Gallatin mb(); 25465e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2547b2fc195eSAndrew Gallatin if (err) { 2548b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2549b2fc195eSAndrew Gallatin } 2550b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2551b2fc195eSAndrew Gallatin /* wait for down irq */ 2552dce01b9bSAndrew Gallatin DELAY(10 * sc->intr_coal_delay); 2553b2fc195eSAndrew Gallatin } 2554b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2555b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2556b2fc195eSAndrew Gallatin } 2557a98d6cd7SAndrew Gallatin 25586d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2559a98d6cd7SAndrew Gallatin 2560b2fc195eSAndrew Gallatin return 0; 2561b2fc195eSAndrew Gallatin } 2562b2fc195eSAndrew Gallatin 2563dce01b9bSAndrew Gallatin static void 2564dce01b9bSAndrew Gallatin mxge_setup_cfg_space(mxge_softc_t *sc) 2565dce01b9bSAndrew Gallatin { 2566dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2567dce01b9bSAndrew Gallatin int reg; 2568dce01b9bSAndrew Gallatin uint16_t cmd, lnk, pectl; 2569dce01b9bSAndrew Gallatin 2570dce01b9bSAndrew Gallatin /* find the PCIe link width and set max read request to 4KB*/ 2571dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 2572dce01b9bSAndrew Gallatin lnk = pci_read_config(dev, reg + 0x12, 2); 2573dce01b9bSAndrew Gallatin sc->link_width = (lnk >> 4) & 0x3f; 2574dce01b9bSAndrew Gallatin 2575dce01b9bSAndrew Gallatin pectl = pci_read_config(dev, reg + 0x8, 2); 2576dce01b9bSAndrew Gallatin pectl = (pectl & ~0x7000) | (5 << 12); 2577dce01b9bSAndrew Gallatin pci_write_config(dev, reg + 0x8, pectl, 2); 2578dce01b9bSAndrew Gallatin } 2579dce01b9bSAndrew Gallatin 2580dce01b9bSAndrew Gallatin /* Enable DMA and Memory space access */ 2581dce01b9bSAndrew Gallatin pci_enable_busmaster(dev); 2582dce01b9bSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2583dce01b9bSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2584dce01b9bSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2585dce01b9bSAndrew Gallatin } 2586dce01b9bSAndrew Gallatin 2587dce01b9bSAndrew Gallatin static uint32_t 2588dce01b9bSAndrew Gallatin mxge_read_reboot(mxge_softc_t *sc) 2589dce01b9bSAndrew Gallatin { 2590dce01b9bSAndrew Gallatin device_t dev = sc->dev; 2591dce01b9bSAndrew Gallatin uint32_t vs; 2592dce01b9bSAndrew Gallatin 2593dce01b9bSAndrew Gallatin /* find the vendor specific offset */ 2594dce01b9bSAndrew Gallatin if (pci_find_extcap(dev, PCIY_VENDOR, &vs) != 0) { 2595dce01b9bSAndrew Gallatin device_printf(sc->dev, 2596dce01b9bSAndrew Gallatin "could not find vendor specific offset\n"); 2597dce01b9bSAndrew Gallatin return (uint32_t)-1; 2598dce01b9bSAndrew Gallatin } 2599dce01b9bSAndrew Gallatin /* enable read32 mode */ 2600dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x10, 0x3, 1); 2601dce01b9bSAndrew Gallatin /* tell NIC which register to read */ 2602dce01b9bSAndrew Gallatin pci_write_config(dev, vs + 0x18, 0xfffffff0, 4); 2603dce01b9bSAndrew Gallatin return (pci_read_config(dev, vs + 0x14, 4)); 2604dce01b9bSAndrew Gallatin } 2605dce01b9bSAndrew Gallatin 2606dce01b9bSAndrew Gallatin static void 2607dce01b9bSAndrew Gallatin mxge_watchdog_reset(mxge_softc_t *sc) 2608dce01b9bSAndrew Gallatin { 2609dce01b9bSAndrew Gallatin int err; 2610dce01b9bSAndrew Gallatin uint32_t reboot; 2611dce01b9bSAndrew Gallatin uint16_t cmd; 2612dce01b9bSAndrew Gallatin 2613dce01b9bSAndrew Gallatin err = ENXIO; 2614dce01b9bSAndrew Gallatin 2615dce01b9bSAndrew Gallatin device_printf(sc->dev, "Watchdog reset!\n"); 2616dce01b9bSAndrew Gallatin 2617dce01b9bSAndrew Gallatin /* 2618dce01b9bSAndrew Gallatin * check to see if the NIC rebooted. If it did, then all of 2619dce01b9bSAndrew Gallatin * PCI config space has been reset, and things like the 2620dce01b9bSAndrew Gallatin * busmaster bit will be zero. If this is the case, then we 2621dce01b9bSAndrew Gallatin * must restore PCI config space before the NIC can be used 2622dce01b9bSAndrew Gallatin * again 2623dce01b9bSAndrew Gallatin */ 2624dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2625dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2626dce01b9bSAndrew Gallatin /* 2627dce01b9bSAndrew Gallatin * maybe the watchdog caught the NIC rebooting; wait 2628dce01b9bSAndrew Gallatin * up to 100ms for it to finish. If it does not come 2629dce01b9bSAndrew Gallatin * back, then give up 2630dce01b9bSAndrew Gallatin */ 2631dce01b9bSAndrew Gallatin DELAY(1000*100); 2632dce01b9bSAndrew Gallatin cmd = pci_read_config(sc->dev, PCIR_COMMAND, 2); 2633dce01b9bSAndrew Gallatin if (cmd == 0xffff) { 2634dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC disappeared!\n"); 2635dce01b9bSAndrew Gallatin goto abort; 2636dce01b9bSAndrew Gallatin } 2637dce01b9bSAndrew Gallatin } 2638dce01b9bSAndrew Gallatin if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) { 2639dce01b9bSAndrew Gallatin /* print the reboot status */ 2640dce01b9bSAndrew Gallatin reboot = mxge_read_reboot(sc); 2641dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC rebooted, status = 0x%x\n", 2642dce01b9bSAndrew Gallatin reboot); 2643dce01b9bSAndrew Gallatin /* restore PCI configuration space */ 2644dce01b9bSAndrew Gallatin 2645dce01b9bSAndrew Gallatin /* XXXX waiting for pci_cfg_restore() to be exported */ 2646dce01b9bSAndrew Gallatin goto abort; /* just abort for now */ 2647dce01b9bSAndrew Gallatin 2648dce01b9bSAndrew Gallatin /* and redo any changes we made to our config space */ 2649dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2650dce01b9bSAndrew Gallatin } else { 2651dce01b9bSAndrew Gallatin device_printf(sc->dev, "NIC did not reboot, ring state:\n"); 2652dce01b9bSAndrew Gallatin device_printf(sc->dev, "tx.req=%d tx.done=%d\n", 2653dce01b9bSAndrew Gallatin sc->tx.req, sc->tx.done); 2654dce01b9bSAndrew Gallatin device_printf(sc->dev, "pkt_done=%d fw=%d\n", 2655dce01b9bSAndrew Gallatin sc->tx.pkt_done, 2656dce01b9bSAndrew Gallatin be32toh(sc->fw_stats->send_done_count)); 2657dce01b9bSAndrew Gallatin } 2658dce01b9bSAndrew Gallatin 2659dce01b9bSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) { 2660dce01b9bSAndrew Gallatin mxge_close(sc); 2661dce01b9bSAndrew Gallatin err = mxge_open(sc); 2662dce01b9bSAndrew Gallatin } 2663dce01b9bSAndrew Gallatin 2664dce01b9bSAndrew Gallatin abort: 2665dce01b9bSAndrew Gallatin /* 2666dce01b9bSAndrew Gallatin * stop the watchdog if the nic is dead, to avoid spamming the 2667dce01b9bSAndrew Gallatin * console 2668dce01b9bSAndrew Gallatin */ 2669dce01b9bSAndrew Gallatin if (err != 0) { 2670dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2671dce01b9bSAndrew Gallatin } 2672dce01b9bSAndrew Gallatin } 2673dce01b9bSAndrew Gallatin 2674dce01b9bSAndrew Gallatin static void 2675dce01b9bSAndrew Gallatin mxge_watchdog(mxge_softc_t *sc) 2676dce01b9bSAndrew Gallatin { 2677dce01b9bSAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 2678dce01b9bSAndrew Gallatin 2679dce01b9bSAndrew Gallatin /* see if we have outstanding transmits, which 2680dce01b9bSAndrew Gallatin have been pending for more than mxge_ticks */ 2681dce01b9bSAndrew Gallatin if (tx->req != tx->done && 2682dce01b9bSAndrew Gallatin tx->watchdog_req != tx->watchdog_done && 2683dce01b9bSAndrew Gallatin tx->done == tx->watchdog_done) 2684dce01b9bSAndrew Gallatin mxge_watchdog_reset(sc); 2685dce01b9bSAndrew Gallatin 2686dce01b9bSAndrew Gallatin tx->watchdog_req = tx->req; 2687dce01b9bSAndrew Gallatin tx->watchdog_done = tx->done; 2688dce01b9bSAndrew Gallatin } 2689dce01b9bSAndrew Gallatin 2690dce01b9bSAndrew Gallatin static void 2691dce01b9bSAndrew Gallatin mxge_tick(void *arg) 2692dce01b9bSAndrew Gallatin { 2693dce01b9bSAndrew Gallatin mxge_softc_t *sc = arg; 2694dce01b9bSAndrew Gallatin 2695dce01b9bSAndrew Gallatin 2696dce01b9bSAndrew Gallatin /* Synchronize with possible callout reset/stop. */ 2697dce01b9bSAndrew Gallatin if (callout_pending(&sc->co_hdl) || 2698dce01b9bSAndrew Gallatin !callout_active(&sc->co_hdl)) { 2699dce01b9bSAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2700dce01b9bSAndrew Gallatin return; 2701dce01b9bSAndrew Gallatin } 2702dce01b9bSAndrew Gallatin 2703dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2704dce01b9bSAndrew Gallatin mxge_watchdog(sc); 2705dce01b9bSAndrew Gallatin } 2706b2fc195eSAndrew Gallatin 2707b2fc195eSAndrew Gallatin static int 27086d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2709b2fc195eSAndrew Gallatin { 2710b2fc195eSAndrew Gallatin return EINVAL; 2711b2fc195eSAndrew Gallatin } 2712b2fc195eSAndrew Gallatin 2713b2fc195eSAndrew Gallatin static int 27146d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2715b2fc195eSAndrew Gallatin { 2716b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2717b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2718b2fc195eSAndrew Gallatin int err = 0; 2719b2fc195eSAndrew Gallatin 2720b2fc195eSAndrew Gallatin 2721b2fc195eSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN; 27226d87a65dSAndrew Gallatin if ((real_mtu > MXGE_MAX_ETHER_MTU) || 2723b2fc195eSAndrew Gallatin real_mtu < 60) 2724b2fc195eSAndrew Gallatin return EINVAL; 2725a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2726b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2727b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2728b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 2729dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 27306d87a65dSAndrew Gallatin mxge_close(sc); 27316d87a65dSAndrew Gallatin err = mxge_open(sc); 2732b2fc195eSAndrew Gallatin if (err != 0) { 2733b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 27346d87a65dSAndrew Gallatin mxge_close(sc); 27356d87a65dSAndrew Gallatin (void) mxge_open(sc); 2736b2fc195eSAndrew Gallatin } 2737dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, mxge_tick, sc); 2738b2fc195eSAndrew Gallatin } 2739a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2740b2fc195eSAndrew Gallatin return err; 2741b2fc195eSAndrew Gallatin } 2742b2fc195eSAndrew Gallatin 2743b2fc195eSAndrew Gallatin static void 27446d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 2745b2fc195eSAndrew Gallatin { 27466d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2747b2fc195eSAndrew Gallatin 2748b2fc195eSAndrew Gallatin 2749b2fc195eSAndrew Gallatin if (sc == NULL) 2750b2fc195eSAndrew Gallatin return; 2751b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 2752b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 2753b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 2754b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 2755b2fc195eSAndrew Gallatin } 2756b2fc195eSAndrew Gallatin 2757b2fc195eSAndrew Gallatin static int 27586d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2759b2fc195eSAndrew Gallatin { 27606d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2761b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 2762b2fc195eSAndrew Gallatin int err, mask; 2763b2fc195eSAndrew Gallatin 2764b2fc195eSAndrew Gallatin err = 0; 2765b2fc195eSAndrew Gallatin switch (command) { 2766b2fc195eSAndrew Gallatin case SIOCSIFADDR: 2767b2fc195eSAndrew Gallatin case SIOCGIFADDR: 2768b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 2769b2fc195eSAndrew Gallatin break; 2770b2fc195eSAndrew Gallatin 2771b2fc195eSAndrew Gallatin case SIOCSIFMTU: 27726d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 2773b2fc195eSAndrew Gallatin break; 2774b2fc195eSAndrew Gallatin 2775b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 2776a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2777b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 2778dce01b9bSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 27796d87a65dSAndrew Gallatin err = mxge_open(sc); 2780dce01b9bSAndrew Gallatin callout_reset(&sc->co_hdl, mxge_ticks, 2781dce01b9bSAndrew Gallatin mxge_tick, sc); 2782dce01b9bSAndrew Gallatin } else { 27830fa7f681SAndrew Gallatin /* take care of promis can allmulti 27840fa7f681SAndrew Gallatin flag chages */ 27850fa7f681SAndrew Gallatin mxge_change_promisc(sc, 27860fa7f681SAndrew Gallatin ifp->if_flags & IFF_PROMISC); 27870fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 27880fa7f681SAndrew Gallatin } 2789b2fc195eSAndrew Gallatin } else { 2790dce01b9bSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 27916d87a65dSAndrew Gallatin mxge_close(sc); 2792dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 2793dce01b9bSAndrew Gallatin } 2794b2fc195eSAndrew Gallatin } 2795a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2796b2fc195eSAndrew Gallatin break; 2797b2fc195eSAndrew Gallatin 2798b2fc195eSAndrew Gallatin case SIOCADDMULTI: 2799b2fc195eSAndrew Gallatin case SIOCDELMULTI: 2800a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 28010fa7f681SAndrew Gallatin mxge_set_multicast_list(sc); 2802a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2803b2fc195eSAndrew Gallatin break; 2804b2fc195eSAndrew Gallatin 2805b2fc195eSAndrew Gallatin case SIOCSIFCAP: 2806a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 2807b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2808b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 2809b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 2810aed8e389SAndrew Gallatin ifp->if_capenable &= ~(IFCAP_TXCSUM|IFCAP_TSO4); 2811aed8e389SAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP 2812aed8e389SAndrew Gallatin | CSUM_TSO); 2813b2fc195eSAndrew Gallatin } else { 2814b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 2815b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 2816b2fc195eSAndrew Gallatin } 2817b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 2818b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 2819b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 28205e7d8541SAndrew Gallatin sc->csum_flag = 0; 2821b2fc195eSAndrew Gallatin } else { 2822b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 28235e7d8541SAndrew Gallatin sc->csum_flag = 1; 2824b2fc195eSAndrew Gallatin } 2825b2fc195eSAndrew Gallatin } 2826aed8e389SAndrew Gallatin if (mask & IFCAP_TSO4) { 2827aed8e389SAndrew Gallatin if (IFCAP_TSO4 & ifp->if_capenable) { 2828aed8e389SAndrew Gallatin ifp->if_capenable &= ~IFCAP_TSO4; 2829aed8e389SAndrew Gallatin ifp->if_hwassist &= ~CSUM_TSO; 2830aed8e389SAndrew Gallatin } else if (IFCAP_TXCSUM & ifp->if_capenable) { 2831aed8e389SAndrew Gallatin ifp->if_capenable |= IFCAP_TSO4; 2832aed8e389SAndrew Gallatin ifp->if_hwassist |= CSUM_TSO; 2833aed8e389SAndrew Gallatin } else { 2834aed8e389SAndrew Gallatin printf("mxge requires tx checksum offload" 2835aed8e389SAndrew Gallatin " be enabled to use TSO\n"); 2836aed8e389SAndrew Gallatin err = EINVAL; 2837aed8e389SAndrew Gallatin } 2838aed8e389SAndrew Gallatin } 2839a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 2840b2fc195eSAndrew Gallatin break; 2841b2fc195eSAndrew Gallatin 2842b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 2843b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 2844b2fc195eSAndrew Gallatin &sc->media, command); 2845b2fc195eSAndrew Gallatin break; 2846b2fc195eSAndrew Gallatin 2847b2fc195eSAndrew Gallatin default: 2848b2fc195eSAndrew Gallatin err = ENOTTY; 2849b2fc195eSAndrew Gallatin } 2850b2fc195eSAndrew Gallatin return err; 2851b2fc195eSAndrew Gallatin } 2852b2fc195eSAndrew Gallatin 2853b2fc195eSAndrew Gallatin static void 28546d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 2855b2fc195eSAndrew Gallatin { 2856b2fc195eSAndrew Gallatin 28576d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 28586d87a65dSAndrew Gallatin &mxge_flow_control); 28596d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 28606d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 28616d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 28626d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 2863d91b1b49SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.force_firmware", 2864d91b1b49SAndrew Gallatin &mxge_force_firmware); 28655e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 28665e7d8541SAndrew Gallatin &mxge_deassert_wait); 28675e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 28685e7d8541SAndrew Gallatin &mxge_verbose); 2869dce01b9bSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.ticks", &mxge_ticks); 2870b2fc195eSAndrew Gallatin 28715e7d8541SAndrew Gallatin if (bootverbose) 28725e7d8541SAndrew Gallatin mxge_verbose = 1; 28736d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 28746d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 2875dce01b9bSAndrew Gallatin if (mxge_ticks == 0) 2876dce01b9bSAndrew Gallatin mxge_ticks = hz; 28776d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 2878b2fc195eSAndrew Gallatin } 2879b2fc195eSAndrew Gallatin 2880b2fc195eSAndrew Gallatin static int 28816d87a65dSAndrew Gallatin mxge_attach(device_t dev) 2882b2fc195eSAndrew Gallatin { 28836d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2884b2fc195eSAndrew Gallatin struct ifnet *ifp; 2885b2fc195eSAndrew Gallatin size_t bytes; 2886dce01b9bSAndrew Gallatin int count, rid, err; 2887b2fc195eSAndrew Gallatin 2888b2fc195eSAndrew Gallatin sc->dev = dev; 28896d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 2890b2fc195eSAndrew Gallatin 2891b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 2892b2fc195eSAndrew Gallatin 1, /* alignment */ 2893b2fc195eSAndrew Gallatin 4096, /* boundary */ 2894b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2895b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2896b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2897aed8e389SAndrew Gallatin 65536 + 256, /* maxsize */ 28985e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 2899b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2900b2fc195eSAndrew Gallatin 0, /* flags */ 2901b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2902b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 2903b2fc195eSAndrew Gallatin 2904b2fc195eSAndrew Gallatin if (err != 0) { 2905b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 2906b2fc195eSAndrew Gallatin err); 2907b2fc195eSAndrew Gallatin goto abort_with_nothing; 2908b2fc195eSAndrew Gallatin } 2909b2fc195eSAndrew Gallatin 2910b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 2911b2fc195eSAndrew Gallatin if (ifp == NULL) { 2912b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 2913b2fc195eSAndrew Gallatin err = ENOSPC; 2914b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 2915b2fc195eSAndrew Gallatin } 2916a98d6cd7SAndrew Gallatin snprintf(sc->cmd_mtx_name, sizeof(sc->cmd_mtx_name), "%s:cmd", 2917a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 2918a98d6cd7SAndrew Gallatin mtx_init(&sc->cmd_mtx, sc->cmd_mtx_name, NULL, MTX_DEF); 2919a98d6cd7SAndrew Gallatin snprintf(sc->tx_mtx_name, sizeof(sc->tx_mtx_name), "%s:tx", 2920a98d6cd7SAndrew Gallatin device_get_nameunit(dev)); 2921a98d6cd7SAndrew Gallatin mtx_init(&sc->tx_mtx, sc->tx_mtx_name, NULL, MTX_DEF); 2922a98d6cd7SAndrew Gallatin snprintf(sc->driver_mtx_name, sizeof(sc->driver_mtx_name), 2923a98d6cd7SAndrew Gallatin "%s:drv", device_get_nameunit(dev)); 2924a98d6cd7SAndrew Gallatin mtx_init(&sc->driver_mtx, sc->driver_mtx_name, 2925b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2926b2fc195eSAndrew Gallatin 2927dce01b9bSAndrew Gallatin callout_init_mtx(&sc->co_hdl, &sc->driver_mtx, 0); 2928d91b1b49SAndrew Gallatin 2929dce01b9bSAndrew Gallatin mxge_setup_cfg_space(sc); 2930b2fc195eSAndrew Gallatin 2931b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 2932b2fc195eSAndrew Gallatin rid = PCIR_BARS; 2933b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 2934b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 2935b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 2936b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 2937b2fc195eSAndrew Gallatin err = ENXIO; 2938b2fc195eSAndrew Gallatin goto abort_with_lock; 2939b2fc195eSAndrew Gallatin } 2940b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 2941b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 2942b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 2943b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 2944b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 2945b2fc195eSAndrew Gallatin err = ENXIO; 2946b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2947b2fc195eSAndrew Gallatin } 2948b2fc195eSAndrew Gallatin 2949b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 2950b2fc195eSAndrew Gallatin lanai SRAM */ 29516d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 2952b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 2953b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 29546d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 2955b2fc195eSAndrew Gallatin sc->eeprom_strings, 29566d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 29576d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 2958b2fc195eSAndrew Gallatin if (err != 0) 2959b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2960b2fc195eSAndrew Gallatin 2961b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 29626d87a65dSAndrew Gallatin mxge_enable_wc(sc); 2963b2fc195eSAndrew Gallatin 2964b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 29656d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 29666d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 2967b2fc195eSAndrew Gallatin if (err != 0) 2968b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2969b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 29706d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 2971b2fc195eSAndrew Gallatin if (err != 0) 2972b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 2973b2fc195eSAndrew Gallatin 29746d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 2975b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 2976b2fc195eSAndrew Gallatin if (err != 0) 2977b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 29785e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 2979b2fc195eSAndrew Gallatin 2980a98d6cd7SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->dmabench_dma, 4096, 4096); 2981a98d6cd7SAndrew Gallatin if (err != 0) 2982a98d6cd7SAndrew Gallatin goto abort_with_fw_stats; 2983b2fc195eSAndrew Gallatin 2984b2fc195eSAndrew Gallatin /* allocate interrupt queues */ 29855e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry); 29865e7d8541SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2987b2fc195eSAndrew Gallatin if (err != 0) 2988a98d6cd7SAndrew Gallatin goto abort_with_dmabench; 29895e7d8541SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 29905e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2991dc8731d4SAndrew Gallatin 2992b2fc195eSAndrew Gallatin /* Add our ithread */ 2993dc8731d4SAndrew Gallatin count = pci_msi_count(dev); 2994dc8731d4SAndrew Gallatin if (count == 1 && pci_alloc_msi(dev, &count) == 0) { 2995dc8731d4SAndrew Gallatin rid = 1; 2996dc8731d4SAndrew Gallatin sc->msi_enabled = 1; 2997dc8731d4SAndrew Gallatin } else { 2998b2fc195eSAndrew Gallatin rid = 0; 2999dc8731d4SAndrew Gallatin } 3000b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 3001b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 3002b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 3003b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 30045e7d8541SAndrew Gallatin goto abort_with_rx_done; 3005b2fc195eSAndrew Gallatin } 3006d91b1b49SAndrew Gallatin if (mxge_verbose) 3007dc8731d4SAndrew Gallatin device_printf(dev, "using %s irq %ld\n", 3008dc8731d4SAndrew Gallatin sc->msi_enabled ? "MSI" : "INTx", 3009dc8731d4SAndrew Gallatin rman_get_start(sc->irq_res)); 3010b2fc195eSAndrew Gallatin /* load the firmware */ 30116d87a65dSAndrew Gallatin mxge_select_firmware(sc); 3012b2fc195eSAndrew Gallatin 30136d87a65dSAndrew Gallatin err = mxge_load_firmware(sc); 3014b2fc195eSAndrew Gallatin if (err != 0) 3015b2fc195eSAndrew Gallatin goto abort_with_irq_res; 30165e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 30176d87a65dSAndrew Gallatin err = mxge_reset(sc); 3018b2fc195eSAndrew Gallatin if (err != 0) 3019b2fc195eSAndrew Gallatin goto abort_with_irq_res; 3020b2fc195eSAndrew Gallatin 3021a98d6cd7SAndrew Gallatin err = mxge_alloc_rings(sc); 3022a98d6cd7SAndrew Gallatin if (err != 0) { 3023a98d6cd7SAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 3024a98d6cd7SAndrew Gallatin goto abort_with_irq_res; 3025a98d6cd7SAndrew Gallatin } 3026a98d6cd7SAndrew Gallatin 3027a98d6cd7SAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 3028a98d6cd7SAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 3029ef544f63SPaolo Pisati NULL, mxge_intr, sc, &sc->ih); 3030a98d6cd7SAndrew Gallatin if (err != 0) { 3031a98d6cd7SAndrew Gallatin goto abort_with_rings; 3032a98d6cd7SAndrew Gallatin } 3033b2fc195eSAndrew Gallatin /* hook into the network stack */ 3034b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 3035b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 3036a82c2581SAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_TSO4 | 3037a82c2581SAndrew Gallatin IFCAP_JUMBO_MTU; 3038aed8e389SAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO; 3039b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 30405e7d8541SAndrew Gallatin sc->csum_flag = 1; 30416d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 3042b2fc195eSAndrew Gallatin ifp->if_softc = sc; 3043b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 30446d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 30456d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 3046b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 3047b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 30486d87a65dSAndrew Gallatin ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; 3049b2fc195eSAndrew Gallatin 3050b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 30516d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 30526d87a65dSAndrew Gallatin mxge_media_status); 3053b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 30546d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 3055b2fc195eSAndrew Gallatin return 0; 3056b2fc195eSAndrew Gallatin 3057a98d6cd7SAndrew Gallatin abort_with_rings: 3058a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3059b2fc195eSAndrew Gallatin abort_with_irq_res: 3060dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3061dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3062dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3063dc8731d4SAndrew Gallatin pci_release_msi(dev); 30645e7d8541SAndrew Gallatin abort_with_rx_done: 30655e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 30665e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 3067a98d6cd7SAndrew Gallatin abort_with_dmabench: 3068a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 30695e7d8541SAndrew Gallatin abort_with_fw_stats: 30706d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3071b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 30726d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 3073b2fc195eSAndrew Gallatin abort_with_cmd_dma: 30746d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3075b2fc195eSAndrew Gallatin abort_with_mem_res: 3076b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3077b2fc195eSAndrew Gallatin abort_with_lock: 3078b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3079a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3080a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3081a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3082b2fc195eSAndrew Gallatin if_free(ifp); 3083b2fc195eSAndrew Gallatin abort_with_parent_dmat: 3084b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3085b2fc195eSAndrew Gallatin 3086b2fc195eSAndrew Gallatin abort_with_nothing: 3087b2fc195eSAndrew Gallatin return err; 3088b2fc195eSAndrew Gallatin } 3089b2fc195eSAndrew Gallatin 3090b2fc195eSAndrew Gallatin static int 30916d87a65dSAndrew Gallatin mxge_detach(device_t dev) 3092b2fc195eSAndrew Gallatin { 30936d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 3094b2fc195eSAndrew Gallatin 3095a98d6cd7SAndrew Gallatin mtx_lock(&sc->driver_mtx); 3096b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 30976d87a65dSAndrew Gallatin mxge_close(sc); 3098dce01b9bSAndrew Gallatin callout_stop(&sc->co_hdl); 3099a98d6cd7SAndrew Gallatin mtx_unlock(&sc->driver_mtx); 3100b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 3101dce01b9bSAndrew Gallatin ifmedia_removeall(&sc->media); 3102091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 3103a98d6cd7SAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 3104a98d6cd7SAndrew Gallatin mxge_free_rings(sc); 3105dc8731d4SAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 3106dc8731d4SAndrew Gallatin sc->msi_enabled ? 1 : 0, sc->irq_res); 3107dc8731d4SAndrew Gallatin if (sc->msi_enabled) 3108dc8731d4SAndrew Gallatin pci_release_msi(dev); 3109dc8731d4SAndrew Gallatin 31105e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 31115e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 31126d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 3113a98d6cd7SAndrew Gallatin mxge_dma_free(&sc->dmabench_dma); 31146d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 31156d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 3116b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 3117b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 3118a98d6cd7SAndrew Gallatin mtx_destroy(&sc->cmd_mtx); 3119a98d6cd7SAndrew Gallatin mtx_destroy(&sc->tx_mtx); 3120a98d6cd7SAndrew Gallatin mtx_destroy(&sc->driver_mtx); 3121b2fc195eSAndrew Gallatin if_free(sc->ifp); 3122b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 3123b2fc195eSAndrew Gallatin return 0; 3124b2fc195eSAndrew Gallatin } 3125b2fc195eSAndrew Gallatin 3126b2fc195eSAndrew Gallatin static int 31276d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 3128b2fc195eSAndrew Gallatin { 3129b2fc195eSAndrew Gallatin return 0; 3130b2fc195eSAndrew Gallatin } 3131b2fc195eSAndrew Gallatin 3132b2fc195eSAndrew Gallatin /* 3133b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 3134b2fc195eSAndrew Gallatin 3135b2fc195eSAndrew Gallatin Local Variables: 3136b2fc195eSAndrew Gallatin c-file-style:"linux" 3137b2fc195eSAndrew Gallatin tab-width:8 3138b2fc195eSAndrew Gallatin End: 3139b2fc195eSAndrew Gallatin */ 3140