16d87a65dSAndrew Gallatin /****************************************************************************** 2b2fc195eSAndrew Gallatin 3b2fc195eSAndrew Gallatin Copyright (c) 2006, Myricom Inc. 4b2fc195eSAndrew Gallatin All rights reserved. 5b2fc195eSAndrew Gallatin 6b2fc195eSAndrew Gallatin Redistribution and use in source and binary forms, with or without 7b2fc195eSAndrew Gallatin modification, are permitted provided that the following conditions are met: 8b2fc195eSAndrew Gallatin 9b2fc195eSAndrew Gallatin 1. Redistributions of source code must retain the above copyright notice, 10b2fc195eSAndrew Gallatin this list of conditions and the following disclaimer. 11b2fc195eSAndrew Gallatin 12b2fc195eSAndrew Gallatin 2. Redistributions in binary form must reproduce the above copyright 13b2fc195eSAndrew Gallatin notice, this list of conditions and the following disclaimer in the 14b2fc195eSAndrew Gallatin documentation and/or other materials provided with the distribution. 15b2fc195eSAndrew Gallatin 16b2fc195eSAndrew Gallatin 3. Neither the name of the Myricom Inc, nor the names of its 17b2fc195eSAndrew Gallatin contributors may be used to endorse or promote products derived from 18b2fc195eSAndrew Gallatin this software without specific prior written permission. 19b2fc195eSAndrew Gallatin 20b2fc195eSAndrew Gallatin THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21b2fc195eSAndrew Gallatin AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22b2fc195eSAndrew Gallatin IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23b2fc195eSAndrew Gallatin ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24b2fc195eSAndrew Gallatin LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25b2fc195eSAndrew Gallatin CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26b2fc195eSAndrew Gallatin SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27b2fc195eSAndrew Gallatin INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28b2fc195eSAndrew Gallatin CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29b2fc195eSAndrew Gallatin ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30b2fc195eSAndrew Gallatin POSSIBILITY OF SUCH DAMAGE. 31b2fc195eSAndrew Gallatin 32b2fc195eSAndrew Gallatin ***************************************************************************/ 33b2fc195eSAndrew Gallatin 34b2fc195eSAndrew Gallatin #include <sys/cdefs.h> 35b2fc195eSAndrew Gallatin __FBSDID("$FreeBSD$"); 36b2fc195eSAndrew Gallatin 37b2fc195eSAndrew Gallatin #include <sys/param.h> 38b2fc195eSAndrew Gallatin #include <sys/systm.h> 39b2fc195eSAndrew Gallatin #include <sys/linker.h> 40b2fc195eSAndrew Gallatin #include <sys/firmware.h> 41b2fc195eSAndrew Gallatin #include <sys/endian.h> 42b2fc195eSAndrew Gallatin #include <sys/sockio.h> 43b2fc195eSAndrew Gallatin #include <sys/mbuf.h> 44b2fc195eSAndrew Gallatin #include <sys/malloc.h> 45b2fc195eSAndrew Gallatin #include <sys/kdb.h> 46b2fc195eSAndrew Gallatin #include <sys/kernel.h> 47b2fc195eSAndrew Gallatin #include <sys/module.h> 48b2fc195eSAndrew Gallatin #include <sys/memrange.h> 49b2fc195eSAndrew Gallatin #include <sys/socket.h> 50b2fc195eSAndrew Gallatin #include <sys/sysctl.h> 51b2fc195eSAndrew Gallatin #include <sys/sx.h> 52b2fc195eSAndrew Gallatin 53b2fc195eSAndrew Gallatin #include <net/if.h> 54b2fc195eSAndrew Gallatin #include <net/if_arp.h> 55b2fc195eSAndrew Gallatin #include <net/ethernet.h> 56b2fc195eSAndrew Gallatin #include <net/if_dl.h> 57b2fc195eSAndrew Gallatin #include <net/if_media.h> 58b2fc195eSAndrew Gallatin 59b2fc195eSAndrew Gallatin #include <net/bpf.h> 60b2fc195eSAndrew Gallatin 61b2fc195eSAndrew Gallatin #include <net/if_types.h> 62b2fc195eSAndrew Gallatin #include <net/if_vlan_var.h> 63b2fc195eSAndrew Gallatin #include <net/zlib.h> 64b2fc195eSAndrew Gallatin 65b2fc195eSAndrew Gallatin #include <netinet/in_systm.h> 66b2fc195eSAndrew Gallatin #include <netinet/in.h> 67b2fc195eSAndrew Gallatin #include <netinet/ip.h> 68b2fc195eSAndrew Gallatin 69b2fc195eSAndrew Gallatin #include <machine/bus.h> 70b2fc195eSAndrew Gallatin #include <machine/resource.h> 71b2fc195eSAndrew Gallatin #include <sys/bus.h> 72b2fc195eSAndrew Gallatin #include <sys/rman.h> 73b2fc195eSAndrew Gallatin 74b2fc195eSAndrew Gallatin #include <dev/pci/pcireg.h> 75b2fc195eSAndrew Gallatin #include <dev/pci/pcivar.h> 76b2fc195eSAndrew Gallatin 77b2fc195eSAndrew Gallatin #include <vm/vm.h> /* for pmap_mapdev() */ 78b2fc195eSAndrew Gallatin #include <vm/pmap.h> 79b2fc195eSAndrew Gallatin 806d87a65dSAndrew Gallatin #include <dev/mxge/mxge_mcp.h> 816d87a65dSAndrew Gallatin #include <dev/mxge/mcp_gen_header.h> 826d87a65dSAndrew Gallatin #include <dev/mxge/if_mxge_var.h> 83b2fc195eSAndrew Gallatin 84b2fc195eSAndrew Gallatin /* tunable params */ 856d87a65dSAndrew Gallatin static int mxge_nvidia_ecrc_enable = 1; 865e7d8541SAndrew Gallatin static int mxge_max_intr_slots = 1024; 876d87a65dSAndrew Gallatin static int mxge_intr_coal_delay = 30; 885e7d8541SAndrew Gallatin static int mxge_deassert_wait = 1; 896d87a65dSAndrew Gallatin static int mxge_flow_control = 1; 905e7d8541SAndrew Gallatin static int mxge_verbose = 0; 916d87a65dSAndrew Gallatin static char *mxge_fw_unaligned = "mxge_ethp_z8e"; 926d87a65dSAndrew Gallatin static char *mxge_fw_aligned = "mxge_eth_z8e"; 93b2fc195eSAndrew Gallatin 946d87a65dSAndrew Gallatin static int mxge_probe(device_t dev); 956d87a65dSAndrew Gallatin static int mxge_attach(device_t dev); 966d87a65dSAndrew Gallatin static int mxge_detach(device_t dev); 976d87a65dSAndrew Gallatin static int mxge_shutdown(device_t dev); 986d87a65dSAndrew Gallatin static void mxge_intr(void *arg); 99b2fc195eSAndrew Gallatin 1006d87a65dSAndrew Gallatin static device_method_t mxge_methods[] = 101b2fc195eSAndrew Gallatin { 102b2fc195eSAndrew Gallatin /* Device interface */ 1036d87a65dSAndrew Gallatin DEVMETHOD(device_probe, mxge_probe), 1046d87a65dSAndrew Gallatin DEVMETHOD(device_attach, mxge_attach), 1056d87a65dSAndrew Gallatin DEVMETHOD(device_detach, mxge_detach), 1066d87a65dSAndrew Gallatin DEVMETHOD(device_shutdown, mxge_shutdown), 107b2fc195eSAndrew Gallatin {0, 0} 108b2fc195eSAndrew Gallatin }; 109b2fc195eSAndrew Gallatin 1106d87a65dSAndrew Gallatin static driver_t mxge_driver = 111b2fc195eSAndrew Gallatin { 1126d87a65dSAndrew Gallatin "mxge", 1136d87a65dSAndrew Gallatin mxge_methods, 1146d87a65dSAndrew Gallatin sizeof(mxge_softc_t), 115b2fc195eSAndrew Gallatin }; 116b2fc195eSAndrew Gallatin 1176d87a65dSAndrew Gallatin static devclass_t mxge_devclass; 118b2fc195eSAndrew Gallatin 119b2fc195eSAndrew Gallatin /* Declare ourselves to be a child of the PCI bus.*/ 1206d87a65dSAndrew Gallatin DRIVER_MODULE(mxge, pci, mxge_driver, mxge_devclass, 0, 0); 1216d87a65dSAndrew Gallatin MODULE_DEPEND(mxge, firmware, 1, 1, 1); 122b2fc195eSAndrew Gallatin 123b2fc195eSAndrew Gallatin static int 1246d87a65dSAndrew Gallatin mxge_probe(device_t dev) 125b2fc195eSAndrew Gallatin { 1266d87a65dSAndrew Gallatin if ((pci_get_vendor(dev) == MXGE_PCI_VENDOR_MYRICOM) && 1276d87a65dSAndrew Gallatin (pci_get_device(dev) == MXGE_PCI_DEVICE_Z8E)) { 128b2fc195eSAndrew Gallatin device_set_desc(dev, "Myri10G-PCIE-8A"); 129b2fc195eSAndrew Gallatin return 0; 130b2fc195eSAndrew Gallatin } 131b2fc195eSAndrew Gallatin return ENXIO; 132b2fc195eSAndrew Gallatin } 133b2fc195eSAndrew Gallatin 134b2fc195eSAndrew Gallatin static void 1356d87a65dSAndrew Gallatin mxge_enable_wc(mxge_softc_t *sc) 136b2fc195eSAndrew Gallatin { 137b2fc195eSAndrew Gallatin struct mem_range_desc mrdesc; 138b2fc195eSAndrew Gallatin vm_paddr_t pa; 139b2fc195eSAndrew Gallatin vm_offset_t len; 140b2fc195eSAndrew Gallatin int err, action; 141b2fc195eSAndrew Gallatin 142b2fc195eSAndrew Gallatin pa = rman_get_start(sc->mem_res); 143b2fc195eSAndrew Gallatin len = rman_get_size(sc->mem_res); 144b2fc195eSAndrew Gallatin mrdesc.mr_base = pa; 145b2fc195eSAndrew Gallatin mrdesc.mr_len = len; 146b2fc195eSAndrew Gallatin mrdesc.mr_flags = MDF_WRITECOMBINE; 147b2fc195eSAndrew Gallatin action = MEMRANGE_SET_UPDATE; 1486d87a65dSAndrew Gallatin strcpy((char *)&mrdesc.mr_owner, "mxge"); 149b2fc195eSAndrew Gallatin err = mem_range_attr_set(&mrdesc, &action); 150b2fc195eSAndrew Gallatin if (err != 0) { 151b2fc195eSAndrew Gallatin device_printf(sc->dev, 152b2fc195eSAndrew Gallatin "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", 153b2fc195eSAndrew Gallatin (unsigned long)pa, (unsigned long)len, err); 154b2fc195eSAndrew Gallatin } else { 155b2fc195eSAndrew Gallatin sc->wc = 1; 156b2fc195eSAndrew Gallatin } 157b2fc195eSAndrew Gallatin } 158b2fc195eSAndrew Gallatin 159b2fc195eSAndrew Gallatin 160b2fc195eSAndrew Gallatin /* callback to get our DMA address */ 161b2fc195eSAndrew Gallatin static void 1626d87a65dSAndrew Gallatin mxge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, 163b2fc195eSAndrew Gallatin int error) 164b2fc195eSAndrew Gallatin { 165b2fc195eSAndrew Gallatin if (error == 0) { 166b2fc195eSAndrew Gallatin *(bus_addr_t *) arg = segs->ds_addr; 167b2fc195eSAndrew Gallatin } 168b2fc195eSAndrew Gallatin } 169b2fc195eSAndrew Gallatin 170b2fc195eSAndrew Gallatin static int 1716d87a65dSAndrew Gallatin mxge_dma_alloc(mxge_softc_t *sc, mxge_dma_t *dma, size_t bytes, 172b2fc195eSAndrew Gallatin bus_size_t alignment) 173b2fc195eSAndrew Gallatin { 174b2fc195eSAndrew Gallatin int err; 175b2fc195eSAndrew Gallatin device_t dev = sc->dev; 176b2fc195eSAndrew Gallatin 177b2fc195eSAndrew Gallatin /* allocate DMAable memory tags */ 178b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 179b2fc195eSAndrew Gallatin alignment, /* alignment */ 180b2fc195eSAndrew Gallatin 4096, /* boundary */ 181b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 182b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 183b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 184b2fc195eSAndrew Gallatin bytes, /* maxsize */ 185b2fc195eSAndrew Gallatin 1, /* num segs */ 186b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 187b2fc195eSAndrew Gallatin BUS_DMA_COHERENT, /* flags */ 188b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 189b2fc195eSAndrew Gallatin &dma->dmat); /* tag */ 190b2fc195eSAndrew Gallatin if (err != 0) { 191b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc tag (err = %d)\n", err); 192b2fc195eSAndrew Gallatin return err; 193b2fc195eSAndrew Gallatin } 194b2fc195eSAndrew Gallatin 195b2fc195eSAndrew Gallatin /* allocate DMAable memory & map */ 196b2fc195eSAndrew Gallatin err = bus_dmamem_alloc(dma->dmat, &dma->addr, 197b2fc195eSAndrew Gallatin (BUS_DMA_WAITOK | BUS_DMA_COHERENT 198b2fc195eSAndrew Gallatin | BUS_DMA_ZERO), &dma->map); 199b2fc195eSAndrew Gallatin if (err != 0) { 200b2fc195eSAndrew Gallatin device_printf(dev, "couldn't alloc mem (err = %d)\n", err); 201b2fc195eSAndrew Gallatin goto abort_with_dmat; 202b2fc195eSAndrew Gallatin } 203b2fc195eSAndrew Gallatin 204b2fc195eSAndrew Gallatin /* load the memory */ 205b2fc195eSAndrew Gallatin err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, 2066d87a65dSAndrew Gallatin mxge_dmamap_callback, 207b2fc195eSAndrew Gallatin (void *)&dma->bus_addr, 0); 208b2fc195eSAndrew Gallatin if (err != 0) { 209b2fc195eSAndrew Gallatin device_printf(dev, "couldn't load map (err = %d)\n", err); 210b2fc195eSAndrew Gallatin goto abort_with_mem; 211b2fc195eSAndrew Gallatin } 212b2fc195eSAndrew Gallatin return 0; 213b2fc195eSAndrew Gallatin 214b2fc195eSAndrew Gallatin abort_with_mem: 215b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 216b2fc195eSAndrew Gallatin abort_with_dmat: 217b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 218b2fc195eSAndrew Gallatin return err; 219b2fc195eSAndrew Gallatin } 220b2fc195eSAndrew Gallatin 221b2fc195eSAndrew Gallatin 222b2fc195eSAndrew Gallatin static void 2236d87a65dSAndrew Gallatin mxge_dma_free(mxge_dma_t *dma) 224b2fc195eSAndrew Gallatin { 225b2fc195eSAndrew Gallatin bus_dmamap_unload(dma->dmat, dma->map); 226b2fc195eSAndrew Gallatin bus_dmamem_free(dma->dmat, dma->addr, dma->map); 227b2fc195eSAndrew Gallatin (void)bus_dma_tag_destroy(dma->dmat); 228b2fc195eSAndrew Gallatin } 229b2fc195eSAndrew Gallatin 230b2fc195eSAndrew Gallatin /* 231b2fc195eSAndrew Gallatin * The eeprom strings on the lanaiX have the format 232b2fc195eSAndrew Gallatin * SN=x\0 233b2fc195eSAndrew Gallatin * MAC=x:x:x:x:x:x\0 234b2fc195eSAndrew Gallatin * PC=text\0 235b2fc195eSAndrew Gallatin */ 236b2fc195eSAndrew Gallatin 237b2fc195eSAndrew Gallatin static int 2386d87a65dSAndrew Gallatin mxge_parse_strings(mxge_softc_t *sc) 239b2fc195eSAndrew Gallatin { 2406d87a65dSAndrew Gallatin #define MXGE_NEXT_STRING(p) while(ptr < limit && *ptr++) 241b2fc195eSAndrew Gallatin 242b2fc195eSAndrew Gallatin char *ptr, *limit; 243b2fc195eSAndrew Gallatin int i, found_mac; 244b2fc195eSAndrew Gallatin 245b2fc195eSAndrew Gallatin ptr = sc->eeprom_strings; 2466d87a65dSAndrew Gallatin limit = sc->eeprom_strings + MXGE_EEPROM_STRINGS_SIZE; 247b2fc195eSAndrew Gallatin found_mac = 0; 248b2fc195eSAndrew Gallatin while (ptr < limit && *ptr != '\0') { 249b2fc195eSAndrew Gallatin if (memcmp(ptr, "MAC=", 4) == 0) { 2505e7d8541SAndrew Gallatin ptr += 1; 251b2fc195eSAndrew Gallatin sc->mac_addr_string = ptr; 252b2fc195eSAndrew Gallatin for (i = 0; i < 6; i++) { 2535e7d8541SAndrew Gallatin ptr += 3; 254b2fc195eSAndrew Gallatin if ((ptr + 2) > limit) 255b2fc195eSAndrew Gallatin goto abort; 256b2fc195eSAndrew Gallatin sc->mac_addr[i] = strtoul(ptr, NULL, 16); 257b2fc195eSAndrew Gallatin found_mac = 1; 258b2fc195eSAndrew Gallatin } 2595e7d8541SAndrew Gallatin } else if (memcmp(ptr, "PC=", 3) == 0) { 2605e7d8541SAndrew Gallatin ptr += 3; 2615e7d8541SAndrew Gallatin strncpy(sc->product_code_string, ptr, 2625e7d8541SAndrew Gallatin sizeof (sc->product_code_string) - 1); 2635e7d8541SAndrew Gallatin } else if (memcmp(ptr, "SN=", 3) == 0) { 2645e7d8541SAndrew Gallatin ptr += 3; 2655e7d8541SAndrew Gallatin strncpy(sc->serial_number_string, ptr, 2665e7d8541SAndrew Gallatin sizeof (sc->serial_number_string) - 1); 267b2fc195eSAndrew Gallatin } 2686d87a65dSAndrew Gallatin MXGE_NEXT_STRING(ptr); 269b2fc195eSAndrew Gallatin } 270b2fc195eSAndrew Gallatin 271b2fc195eSAndrew Gallatin if (found_mac) 272b2fc195eSAndrew Gallatin return 0; 273b2fc195eSAndrew Gallatin 274b2fc195eSAndrew Gallatin abort: 275b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to parse eeprom_strings\n"); 276b2fc195eSAndrew Gallatin 277b2fc195eSAndrew Gallatin return ENXIO; 278b2fc195eSAndrew Gallatin } 279b2fc195eSAndrew Gallatin 280b2fc195eSAndrew Gallatin #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ 281b2fc195eSAndrew Gallatin static int 2826d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 283b2fc195eSAndrew Gallatin { 284b2fc195eSAndrew Gallatin uint32_t val; 285b2fc195eSAndrew Gallatin unsigned long off; 286b2fc195eSAndrew Gallatin char *va, *cfgptr; 287b2fc195eSAndrew Gallatin uint16_t vendor_id, device_id; 288b2fc195eSAndrew Gallatin uintptr_t bus, slot, func, ivend, idev; 289b2fc195eSAndrew Gallatin uint32_t *ptr32; 290b2fc195eSAndrew Gallatin 291b2fc195eSAndrew Gallatin /* XXXX 292b2fc195eSAndrew Gallatin Test below is commented because it is believed that doing 293b2fc195eSAndrew Gallatin config read/write beyond 0xff will access the config space 294b2fc195eSAndrew Gallatin for the next larger function. Uncomment this and remove 295b2fc195eSAndrew Gallatin the hacky pmap_mapdev() way of accessing config space when 296b2fc195eSAndrew Gallatin FreeBSD grows support for extended pcie config space access 297b2fc195eSAndrew Gallatin */ 298b2fc195eSAndrew Gallatin #if 0 299b2fc195eSAndrew Gallatin /* See if we can, by some miracle, access the extended 300b2fc195eSAndrew Gallatin config space */ 301b2fc195eSAndrew Gallatin val = pci_read_config(pdev, 0x178, 4); 302b2fc195eSAndrew Gallatin if (val != 0xffffffff) { 303b2fc195eSAndrew Gallatin val |= 0x40; 304b2fc195eSAndrew Gallatin pci_write_config(pdev, 0x178, val, 4); 305b2fc195eSAndrew Gallatin return 0; 306b2fc195eSAndrew Gallatin } 307b2fc195eSAndrew Gallatin #endif 308b2fc195eSAndrew Gallatin /* Rather than using normal pci config space writes, we must 309b2fc195eSAndrew Gallatin * map the Nvidia config space ourselves. This is because on 310b2fc195eSAndrew Gallatin * opteron/nvidia class machine the 0xe000000 mapping is 311b2fc195eSAndrew Gallatin * handled by the nvidia chipset, that means the internal PCI 312b2fc195eSAndrew Gallatin * device (the on-chip northbridge), or the amd-8131 bridge 313b2fc195eSAndrew Gallatin * and things behind them are not visible by this method. 314b2fc195eSAndrew Gallatin */ 315b2fc195eSAndrew Gallatin 316b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 317b2fc195eSAndrew Gallatin PCI_IVAR_BUS, &bus); 318b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 319b2fc195eSAndrew Gallatin PCI_IVAR_SLOT, &slot); 320b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 321b2fc195eSAndrew Gallatin PCI_IVAR_FUNCTION, &func); 322b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 323b2fc195eSAndrew Gallatin PCI_IVAR_VENDOR, &ivend); 324b2fc195eSAndrew Gallatin BUS_READ_IVAR(device_get_parent(pdev), pdev, 325b2fc195eSAndrew Gallatin PCI_IVAR_DEVICE, &idev); 326b2fc195eSAndrew Gallatin 327b2fc195eSAndrew Gallatin off = 0xe0000000UL 328b2fc195eSAndrew Gallatin + 0x00100000UL * (unsigned long)bus 329b2fc195eSAndrew Gallatin + 0x00001000UL * (unsigned long)(func 330b2fc195eSAndrew Gallatin + 8 * slot); 331b2fc195eSAndrew Gallatin 332b2fc195eSAndrew Gallatin /* map it into the kernel */ 333b2fc195eSAndrew Gallatin va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); 334b2fc195eSAndrew Gallatin 335b2fc195eSAndrew Gallatin 336b2fc195eSAndrew Gallatin if (va == NULL) { 337b2fc195eSAndrew Gallatin device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); 338b2fc195eSAndrew Gallatin return EIO; 339b2fc195eSAndrew Gallatin } 340b2fc195eSAndrew Gallatin /* get a pointer to the config space mapped into the kernel */ 341b2fc195eSAndrew Gallatin cfgptr = va + (off & PAGE_MASK); 342b2fc195eSAndrew Gallatin 343b2fc195eSAndrew Gallatin /* make sure that we can really access it */ 344b2fc195eSAndrew Gallatin vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); 345b2fc195eSAndrew Gallatin device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); 346b2fc195eSAndrew Gallatin if (! (vendor_id == ivend && device_id == idev)) { 347b2fc195eSAndrew Gallatin device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", 348b2fc195eSAndrew Gallatin vendor_id, device_id); 349b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 350b2fc195eSAndrew Gallatin return EIO; 351b2fc195eSAndrew Gallatin } 352b2fc195eSAndrew Gallatin 353b2fc195eSAndrew Gallatin ptr32 = (uint32_t*)(cfgptr + 0x178); 354b2fc195eSAndrew Gallatin val = *ptr32; 355b2fc195eSAndrew Gallatin 356b2fc195eSAndrew Gallatin if (val == 0xffffffff) { 357b2fc195eSAndrew Gallatin device_printf(sc->dev, "extended mapping failed\n"); 358b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 359b2fc195eSAndrew Gallatin return EIO; 360b2fc195eSAndrew Gallatin } 361b2fc195eSAndrew Gallatin *ptr32 = val | 0x40; 362b2fc195eSAndrew Gallatin pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); 3635e7d8541SAndrew Gallatin if (mxge_verbose) 364b2fc195eSAndrew Gallatin device_printf(sc->dev, 3655e7d8541SAndrew Gallatin "Enabled ECRC on upstream Nvidia bridge " 3665e7d8541SAndrew Gallatin "at %d:%d:%d\n", 367b2fc195eSAndrew Gallatin (int)bus, (int)slot, (int)func); 368b2fc195eSAndrew Gallatin return 0; 369b2fc195eSAndrew Gallatin } 370b2fc195eSAndrew Gallatin #else 371b2fc195eSAndrew Gallatin static int 3726d87a65dSAndrew Gallatin mxge_enable_nvidia_ecrc(mxge_softc_t *sc, device_t pdev) 373b2fc195eSAndrew Gallatin { 374b2fc195eSAndrew Gallatin device_printf(sc->dev, 375b2fc195eSAndrew Gallatin "Nforce 4 chipset on non-x86/amd64!?!?!\n"); 376b2fc195eSAndrew Gallatin return ENXIO; 377b2fc195eSAndrew Gallatin } 378b2fc195eSAndrew Gallatin #endif 379b2fc195eSAndrew Gallatin /* 380b2fc195eSAndrew Gallatin * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput 381b2fc195eSAndrew Gallatin * when the PCI-E Completion packets are aligned on an 8-byte 382b2fc195eSAndrew Gallatin * boundary. Some PCI-E chip sets always align Completion packets; on 383b2fc195eSAndrew Gallatin * the ones that do not, the alignment can be enforced by enabling 384b2fc195eSAndrew Gallatin * ECRC generation (if supported). 385b2fc195eSAndrew Gallatin * 386b2fc195eSAndrew Gallatin * When PCI-E Completion packets are not aligned, it is actually more 387b2fc195eSAndrew Gallatin * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. 388b2fc195eSAndrew Gallatin * 389b2fc195eSAndrew Gallatin * If the driver can neither enable ECRC nor verify that it has 390b2fc195eSAndrew Gallatin * already been enabled, then it must use a firmware image which works 391b2fc195eSAndrew Gallatin * around unaligned completion packets (ethp_z8e.dat), and it should 392b2fc195eSAndrew Gallatin * also ensure that it never gives the device a Read-DMA which is 393b2fc195eSAndrew Gallatin * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is 394b2fc195eSAndrew Gallatin * enabled, then the driver should use the aligned (eth_z8e.dat) 395b2fc195eSAndrew Gallatin * firmware image, and set tx.boundary to 4KB. 396b2fc195eSAndrew Gallatin */ 397b2fc195eSAndrew Gallatin 398b2fc195eSAndrew Gallatin static void 3996d87a65dSAndrew Gallatin mxge_select_firmware(mxge_softc_t *sc) 400b2fc195eSAndrew Gallatin { 401b2fc195eSAndrew Gallatin int err, aligned = 0; 402b2fc195eSAndrew Gallatin device_t pdev; 403b2fc195eSAndrew Gallatin uint16_t pvend, pdid; 404b2fc195eSAndrew Gallatin 405b2fc195eSAndrew Gallatin pdev = device_get_parent(device_get_parent(sc->dev)); 406b2fc195eSAndrew Gallatin if (pdev == NULL) { 407b2fc195eSAndrew Gallatin device_printf(sc->dev, "could not find parent?\n"); 408b2fc195eSAndrew Gallatin goto abort; 409b2fc195eSAndrew Gallatin } 410b2fc195eSAndrew Gallatin pvend = pci_read_config(pdev, PCIR_VENDOR, 2); 411b2fc195eSAndrew Gallatin pdid = pci_read_config(pdev, PCIR_DEVICE, 2); 412b2fc195eSAndrew Gallatin 413b2fc195eSAndrew Gallatin /* see if we can enable ECRC's on an upstream 414b2fc195eSAndrew Gallatin Nvidia bridge */ 4156d87a65dSAndrew Gallatin if (mxge_nvidia_ecrc_enable && 416b2fc195eSAndrew Gallatin (pvend == 0x10de && pdid == 0x005d)) { 4176d87a65dSAndrew Gallatin err = mxge_enable_nvidia_ecrc(sc, pdev); 418b2fc195eSAndrew Gallatin if (err == 0) { 419b2fc195eSAndrew Gallatin aligned = 1; 4205e7d8541SAndrew Gallatin if (mxge_verbose) 421b2fc195eSAndrew Gallatin device_printf(sc->dev, 4225e7d8541SAndrew Gallatin "Assuming aligned completions" 4235e7d8541SAndrew Gallatin " (ECRC)\n"); 424b2fc195eSAndrew Gallatin } 425b2fc195eSAndrew Gallatin } 426b2fc195eSAndrew Gallatin /* see if the upstream bridge is known to 427b2fc195eSAndrew Gallatin provided aligned completions */ 428b2fc195eSAndrew Gallatin if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) || 429b2fc195eSAndrew Gallatin /* Ontario */ (pvend == 0x10b5 && pdid == 0x8532)) { 4305e7d8541SAndrew Gallatin if (mxge_verbose) 431b2fc195eSAndrew Gallatin device_printf(sc->dev, 4325e7d8541SAndrew Gallatin "Assuming aligned completions " 4335e7d8541SAndrew Gallatin "(0x%x:0x%x)\n", pvend, pdid); 434b2fc195eSAndrew Gallatin } 435b2fc195eSAndrew Gallatin 436b2fc195eSAndrew Gallatin abort: 437b2fc195eSAndrew Gallatin if (aligned) { 4386d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_aligned; 439b2fc195eSAndrew Gallatin sc->tx.boundary = 4096; 440b2fc195eSAndrew Gallatin } else { 4416d87a65dSAndrew Gallatin sc->fw_name = mxge_fw_unaligned; 442b2fc195eSAndrew Gallatin sc->tx.boundary = 2048; 443b2fc195eSAndrew Gallatin } 444b2fc195eSAndrew Gallatin } 445b2fc195eSAndrew Gallatin 446b2fc195eSAndrew Gallatin union qualhack 447b2fc195eSAndrew Gallatin { 448b2fc195eSAndrew Gallatin const char *ro_char; 449b2fc195eSAndrew Gallatin char *rw_char; 450b2fc195eSAndrew Gallatin }; 451b2fc195eSAndrew Gallatin 4524da0d523SAndrew Gallatin static int 4534da0d523SAndrew Gallatin mxge_validate_firmware(mxge_softc_t *sc, const mcp_gen_header_t *hdr) 4544da0d523SAndrew Gallatin { 4554da0d523SAndrew Gallatin int major, minor; 4564da0d523SAndrew Gallatin 4574da0d523SAndrew Gallatin if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { 4584da0d523SAndrew Gallatin device_printf(sc->dev, "Bad firmware type: 0x%x\n", 4594da0d523SAndrew Gallatin be32toh(hdr->mcp_type)); 4604da0d523SAndrew Gallatin return EIO; 4614da0d523SAndrew Gallatin } 4624da0d523SAndrew Gallatin 4634da0d523SAndrew Gallatin /* save firmware version for sysctl */ 4644da0d523SAndrew Gallatin strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); 4654da0d523SAndrew Gallatin if (mxge_verbose) 4664da0d523SAndrew Gallatin device_printf(sc->dev, "firmware id: %s\n", hdr->version); 4674da0d523SAndrew Gallatin 4684da0d523SAndrew Gallatin sscanf(sc->fw_version, "%d.%d", &major, &minor); 4694da0d523SAndrew Gallatin 4704da0d523SAndrew Gallatin if (!(major == MXGEFW_VERSION_MAJOR 4714da0d523SAndrew Gallatin && minor == MXGEFW_VERSION_MINOR)) { 4724da0d523SAndrew Gallatin device_printf(sc->dev, "Found firmware version %s\n", 4734da0d523SAndrew Gallatin sc->fw_version); 4744da0d523SAndrew Gallatin device_printf(sc->dev, "Driver needs %d.%d\n", 4754da0d523SAndrew Gallatin MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); 4764da0d523SAndrew Gallatin return EINVAL; 4774da0d523SAndrew Gallatin } 4784da0d523SAndrew Gallatin return 0; 4794da0d523SAndrew Gallatin 4804da0d523SAndrew Gallatin } 481b2fc195eSAndrew Gallatin 482b2fc195eSAndrew Gallatin static int 4836d87a65dSAndrew Gallatin mxge_load_firmware_helper(mxge_softc_t *sc, uint32_t *limit) 484b2fc195eSAndrew Gallatin { 485b2fc195eSAndrew Gallatin struct firmware *fw; 486b2fc195eSAndrew Gallatin const mcp_gen_header_t *hdr; 487b2fc195eSAndrew Gallatin unsigned hdr_offset; 488b2fc195eSAndrew Gallatin const char *fw_data; 489b2fc195eSAndrew Gallatin union qualhack hack; 490b2fc195eSAndrew Gallatin int status; 4914da0d523SAndrew Gallatin unsigned int i; 4924da0d523SAndrew Gallatin char dummy; 493b2fc195eSAndrew Gallatin 494b2fc195eSAndrew Gallatin 495b2fc195eSAndrew Gallatin fw = firmware_get(sc->fw_name); 496b2fc195eSAndrew Gallatin 497b2fc195eSAndrew Gallatin if (fw == NULL) { 498b2fc195eSAndrew Gallatin device_printf(sc->dev, "Could not find firmware image %s\n", 499b2fc195eSAndrew Gallatin sc->fw_name); 500b2fc195eSAndrew Gallatin return ENOENT; 501b2fc195eSAndrew Gallatin } 502b2fc195eSAndrew Gallatin if (fw->datasize > *limit || 503b2fc195eSAndrew Gallatin fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { 504b2fc195eSAndrew Gallatin device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", 505b2fc195eSAndrew Gallatin sc->fw_name, (int)fw->datasize, (int) *limit); 506b2fc195eSAndrew Gallatin status = ENOSPC; 507b2fc195eSAndrew Gallatin goto abort_with_fw; 508b2fc195eSAndrew Gallatin } 509b2fc195eSAndrew Gallatin *limit = fw->datasize; 510b2fc195eSAndrew Gallatin 511b2fc195eSAndrew Gallatin /* check id */ 512b2fc195eSAndrew Gallatin fw_data = (const char *)fw->data; 513b2fc195eSAndrew Gallatin hdr_offset = htobe32(*(const uint32_t *) 514b2fc195eSAndrew Gallatin (fw_data + MCP_HEADER_PTR_OFFSET)); 515b2fc195eSAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { 516b2fc195eSAndrew Gallatin device_printf(sc->dev, "Bad firmware file"); 517b2fc195eSAndrew Gallatin status = EIO; 518b2fc195eSAndrew Gallatin goto abort_with_fw; 519b2fc195eSAndrew Gallatin } 520b2fc195eSAndrew Gallatin hdr = (const void*)(fw_data + hdr_offset); 521b2fc195eSAndrew Gallatin 5224da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 5234da0d523SAndrew Gallatin if (status != 0) 5244da0d523SAndrew Gallatin goto abort_with_fw; 525b2fc195eSAndrew Gallatin 526b2fc195eSAndrew Gallatin hack.ro_char = fw_data; 527b2fc195eSAndrew Gallatin /* Copy the inflated firmware to NIC SRAM. */ 5284da0d523SAndrew Gallatin for (i = 0; i < *limit; i += 256) { 5294da0d523SAndrew Gallatin mxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, 5304da0d523SAndrew Gallatin hack.rw_char + i, 5314da0d523SAndrew Gallatin min(256U, (unsigned)(*limit - i))); 5324da0d523SAndrew Gallatin mb(); 5334da0d523SAndrew Gallatin dummy = *sc->sram; 5344da0d523SAndrew Gallatin mb(); 5354da0d523SAndrew Gallatin } 536b2fc195eSAndrew Gallatin 537b2fc195eSAndrew Gallatin status = 0; 538b2fc195eSAndrew Gallatin abort_with_fw: 539b2fc195eSAndrew Gallatin firmware_put(fw, FIRMWARE_UNLOAD); 540b2fc195eSAndrew Gallatin return status; 541b2fc195eSAndrew Gallatin } 542b2fc195eSAndrew Gallatin 543b2fc195eSAndrew Gallatin /* 544b2fc195eSAndrew Gallatin * Enable or disable periodic RDMAs from the host to make certain 545b2fc195eSAndrew Gallatin * chipsets resend dropped PCIe messages 546b2fc195eSAndrew Gallatin */ 547b2fc195eSAndrew Gallatin 548b2fc195eSAndrew Gallatin static void 5496d87a65dSAndrew Gallatin mxge_dummy_rdma(mxge_softc_t *sc, int enable) 550b2fc195eSAndrew Gallatin { 551b2fc195eSAndrew Gallatin char buf_bytes[72]; 552b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 553b2fc195eSAndrew Gallatin volatile char *submit; 554b2fc195eSAndrew Gallatin uint32_t *buf, dma_low, dma_high; 555b2fc195eSAndrew Gallatin int i; 556b2fc195eSAndrew Gallatin 557b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 558b2fc195eSAndrew Gallatin 559b2fc195eSAndrew Gallatin /* clear confirmation addr */ 560b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 561b2fc195eSAndrew Gallatin *confirm = 0; 562b2fc195eSAndrew Gallatin mb(); 563b2fc195eSAndrew Gallatin 564b2fc195eSAndrew Gallatin /* send an rdma command to the PCIe engine, and wait for the 565b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 566b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 567b2fc195eSAndrew Gallatin */ 568b2fc195eSAndrew Gallatin 5696d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 5706d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 571b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 572b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 573b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 5746d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); 5756d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); 576b2fc195eSAndrew Gallatin buf[3] = htobe32(dma_high); /* dummy addr MSW */ 577b2fc195eSAndrew Gallatin buf[4] = htobe32(dma_low); /* dummy addr LSW */ 578b2fc195eSAndrew Gallatin buf[5] = htobe32(enable); /* enable? */ 579b2fc195eSAndrew Gallatin 580b2fc195eSAndrew Gallatin 581b2fc195eSAndrew Gallatin submit = (volatile char *)(sc->sram + 0xfc01c0); 582b2fc195eSAndrew Gallatin 5836d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 584b2fc195eSAndrew Gallatin mb(); 585b2fc195eSAndrew Gallatin DELAY(1000); 586b2fc195eSAndrew Gallatin mb(); 587b2fc195eSAndrew Gallatin i = 0; 588b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 589b2fc195eSAndrew Gallatin DELAY(1000); 590b2fc195eSAndrew Gallatin i++; 591b2fc195eSAndrew Gallatin } 592b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 593b2fc195eSAndrew Gallatin device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", 594b2fc195eSAndrew Gallatin (enable ? "enable" : "disable"), confirm, 595b2fc195eSAndrew Gallatin *confirm); 596b2fc195eSAndrew Gallatin } 597b2fc195eSAndrew Gallatin return; 598b2fc195eSAndrew Gallatin } 599b2fc195eSAndrew Gallatin 600b2fc195eSAndrew Gallatin static int 6016d87a65dSAndrew Gallatin mxge_send_cmd(mxge_softc_t *sc, uint32_t cmd, mxge_cmd_t *data) 602b2fc195eSAndrew Gallatin { 603b2fc195eSAndrew Gallatin mcp_cmd_t *buf; 604b2fc195eSAndrew Gallatin char buf_bytes[sizeof(*buf) + 8]; 605b2fc195eSAndrew Gallatin volatile mcp_cmd_response_t *response = sc->cmd; 6065e7d8541SAndrew Gallatin volatile char *cmd_addr = sc->sram + MXGEFW_CMD_OFFSET; 607b2fc195eSAndrew Gallatin uint32_t dma_low, dma_high; 608b2fc195eSAndrew Gallatin int sleep_total = 0; 609b2fc195eSAndrew Gallatin 610b2fc195eSAndrew Gallatin /* ensure buf is aligned to 8 bytes */ 611b2fc195eSAndrew Gallatin buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 612b2fc195eSAndrew Gallatin 613b2fc195eSAndrew Gallatin buf->data0 = htobe32(data->data0); 614b2fc195eSAndrew Gallatin buf->data1 = htobe32(data->data1); 615b2fc195eSAndrew Gallatin buf->data2 = htobe32(data->data2); 616b2fc195eSAndrew Gallatin buf->cmd = htobe32(cmd); 6176d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 6186d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 619b2fc195eSAndrew Gallatin 620b2fc195eSAndrew Gallatin buf->response_addr.low = htobe32(dma_low); 621b2fc195eSAndrew Gallatin buf->response_addr.high = htobe32(dma_high); 622b2fc195eSAndrew Gallatin mtx_lock(&sc->cmd_lock); 623b2fc195eSAndrew Gallatin response->result = 0xffffffff; 624b2fc195eSAndrew Gallatin mb(); 6256d87a65dSAndrew Gallatin mxge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); 626b2fc195eSAndrew Gallatin 6275e7d8541SAndrew Gallatin /* wait up to 20ms */ 6285e7d8541SAndrew Gallatin for (sleep_total = 0; sleep_total < 20; sleep_total++) { 629b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 630b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 631b2fc195eSAndrew Gallatin mb(); 632b2fc195eSAndrew Gallatin if (response->result != 0xffffffff) { 633b2fc195eSAndrew Gallatin if (response->result == 0) { 634b2fc195eSAndrew Gallatin data->data0 = be32toh(response->data); 635b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 636b2fc195eSAndrew Gallatin return 0; 637b2fc195eSAndrew Gallatin } else { 638b2fc195eSAndrew Gallatin device_printf(sc->dev, 6396d87a65dSAndrew Gallatin "mxge: command %d " 640b2fc195eSAndrew Gallatin "failed, result = %d\n", 641b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 642b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 643b2fc195eSAndrew Gallatin return ENXIO; 644b2fc195eSAndrew Gallatin } 645b2fc195eSAndrew Gallatin } 6465e7d8541SAndrew Gallatin DELAY(1000); 647b2fc195eSAndrew Gallatin } 648b2fc195eSAndrew Gallatin mtx_unlock(&sc->cmd_lock); 6496d87a65dSAndrew Gallatin device_printf(sc->dev, "mxge: command %d timed out" 650b2fc195eSAndrew Gallatin "result = %d\n", 651b2fc195eSAndrew Gallatin cmd, be32toh(response->result)); 652b2fc195eSAndrew Gallatin return EAGAIN; 653b2fc195eSAndrew Gallatin } 654b2fc195eSAndrew Gallatin 6554da0d523SAndrew Gallatin static int 6564da0d523SAndrew Gallatin mxge_adopt_running_firmware(mxge_softc_t *sc) 6574da0d523SAndrew Gallatin { 6584da0d523SAndrew Gallatin struct mcp_gen_header *hdr; 6594da0d523SAndrew Gallatin const size_t bytes = sizeof (struct mcp_gen_header); 6604da0d523SAndrew Gallatin size_t hdr_offset; 6614da0d523SAndrew Gallatin int status; 6624da0d523SAndrew Gallatin 6634da0d523SAndrew Gallatin /* find running firmware header */ 6644da0d523SAndrew Gallatin hdr_offset = htobe32(*(volatile uint32_t *) 6654da0d523SAndrew Gallatin (sc->sram + MCP_HEADER_PTR_OFFSET)); 6664da0d523SAndrew Gallatin 6674da0d523SAndrew Gallatin if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > sc->sram_size) { 6684da0d523SAndrew Gallatin device_printf(sc->dev, 6694da0d523SAndrew Gallatin "Running firmware has bad header offset (%d)\n", 6704da0d523SAndrew Gallatin (int)hdr_offset); 6714da0d523SAndrew Gallatin return EIO; 6724da0d523SAndrew Gallatin } 6734da0d523SAndrew Gallatin 6744da0d523SAndrew Gallatin /* copy header of running firmware from SRAM to host memory to 6754da0d523SAndrew Gallatin * validate firmware */ 6764da0d523SAndrew Gallatin hdr = malloc(bytes, M_DEVBUF, M_NOWAIT); 6774da0d523SAndrew Gallatin if (hdr == NULL) { 6784da0d523SAndrew Gallatin device_printf(sc->dev, "could not malloc firmware hdr\n"); 6794da0d523SAndrew Gallatin return ENOMEM; 6804da0d523SAndrew Gallatin } 6814da0d523SAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 6824da0d523SAndrew Gallatin rman_get_bushandle(sc->mem_res), 6834da0d523SAndrew Gallatin hdr_offset, (char *)hdr, bytes); 6844da0d523SAndrew Gallatin status = mxge_validate_firmware(sc, hdr); 6854da0d523SAndrew Gallatin free(hdr, M_DEVBUF); 6864da0d523SAndrew Gallatin return status; 6874da0d523SAndrew Gallatin } 6884da0d523SAndrew Gallatin 689b2fc195eSAndrew Gallatin 690b2fc195eSAndrew Gallatin static int 6916d87a65dSAndrew Gallatin mxge_load_firmware(mxge_softc_t *sc) 692b2fc195eSAndrew Gallatin { 693b2fc195eSAndrew Gallatin volatile uint32_t *confirm; 694b2fc195eSAndrew Gallatin volatile char *submit; 695b2fc195eSAndrew Gallatin char buf_bytes[72]; 696b2fc195eSAndrew Gallatin uint32_t *buf, size, dma_low, dma_high; 697b2fc195eSAndrew Gallatin int status, i; 698b2fc195eSAndrew Gallatin 699b2fc195eSAndrew Gallatin buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); 700b2fc195eSAndrew Gallatin 701b2fc195eSAndrew Gallatin size = sc->sram_size; 7026d87a65dSAndrew Gallatin status = mxge_load_firmware_helper(sc, &size); 703b2fc195eSAndrew Gallatin if (status) { 7044da0d523SAndrew Gallatin /* Try to use the currently running firmware, if 7054da0d523SAndrew Gallatin it is new enough */ 7064da0d523SAndrew Gallatin status = mxge_adopt_running_firmware(sc); 7074da0d523SAndrew Gallatin if (status) { 7084da0d523SAndrew Gallatin device_printf(sc->dev, 7094da0d523SAndrew Gallatin "failed to adopt running firmware\n"); 710b2fc195eSAndrew Gallatin return status; 711b2fc195eSAndrew Gallatin } 7124da0d523SAndrew Gallatin device_printf(sc->dev, 7134da0d523SAndrew Gallatin "Successfully adopted running firmware\n"); 7144da0d523SAndrew Gallatin if (sc->tx.boundary == 4096) { 7154da0d523SAndrew Gallatin device_printf(sc->dev, 7164da0d523SAndrew Gallatin "Using firmware currently running on NIC" 7174da0d523SAndrew Gallatin ". For optimal\n"); 7184da0d523SAndrew Gallatin device_printf(sc->dev, 7194da0d523SAndrew Gallatin "performance consider loading optimized " 7204da0d523SAndrew Gallatin "firmware\n"); 7214da0d523SAndrew Gallatin } 7224da0d523SAndrew Gallatin 7234da0d523SAndrew Gallatin } 724b2fc195eSAndrew Gallatin /* clear confirmation addr */ 725b2fc195eSAndrew Gallatin confirm = (volatile uint32_t *)sc->cmd; 726b2fc195eSAndrew Gallatin *confirm = 0; 727b2fc195eSAndrew Gallatin mb(); 728b2fc195eSAndrew Gallatin /* send a reload command to the bootstrap MCP, and wait for the 729b2fc195eSAndrew Gallatin response in the confirmation address. The firmware should 730b2fc195eSAndrew Gallatin write a -1 there to indicate it is alive and well 731b2fc195eSAndrew Gallatin */ 732b2fc195eSAndrew Gallatin 7336d87a65dSAndrew Gallatin dma_low = MXGE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); 7346d87a65dSAndrew Gallatin dma_high = MXGE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); 735b2fc195eSAndrew Gallatin 736b2fc195eSAndrew Gallatin buf[0] = htobe32(dma_high); /* confirm addr MSW */ 737b2fc195eSAndrew Gallatin buf[1] = htobe32(dma_low); /* confirm addr LSW */ 738b2fc195eSAndrew Gallatin buf[2] = htobe32(0xffffffff); /* confirm data */ 739b2fc195eSAndrew Gallatin 740b2fc195eSAndrew Gallatin /* FIX: All newest firmware should un-protect the bottom of 741b2fc195eSAndrew Gallatin the sram before handoff. However, the very first interfaces 742b2fc195eSAndrew Gallatin do not. Therefore the handoff copy must skip the first 8 bytes 743b2fc195eSAndrew Gallatin */ 744b2fc195eSAndrew Gallatin /* where the code starts*/ 7456d87a65dSAndrew Gallatin buf[3] = htobe32(MXGE_FW_OFFSET + 8); 746b2fc195eSAndrew Gallatin buf[4] = htobe32(size - 8); /* length of code */ 747b2fc195eSAndrew Gallatin buf[5] = htobe32(8); /* where to copy to */ 748b2fc195eSAndrew Gallatin buf[6] = htobe32(0); /* where to jump to */ 749b2fc195eSAndrew Gallatin 750b2fc195eSAndrew Gallatin submit = (volatile char *)(sc->sram + 0xfc0000); 7516d87a65dSAndrew Gallatin mxge_pio_copy(submit, buf, 64); 752b2fc195eSAndrew Gallatin mb(); 753b2fc195eSAndrew Gallatin DELAY(1000); 754b2fc195eSAndrew Gallatin mb(); 755b2fc195eSAndrew Gallatin i = 0; 756b2fc195eSAndrew Gallatin while (*confirm != 0xffffffff && i < 20) { 757b2fc195eSAndrew Gallatin DELAY(1000*10); 758b2fc195eSAndrew Gallatin i++; 759b2fc195eSAndrew Gallatin bus_dmamap_sync(sc->cmd_dma.dmat, 760b2fc195eSAndrew Gallatin sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); 761b2fc195eSAndrew Gallatin } 762b2fc195eSAndrew Gallatin if (*confirm != 0xffffffff) { 763b2fc195eSAndrew Gallatin device_printf(sc->dev,"handoff failed (%p = 0x%x)", 764b2fc195eSAndrew Gallatin confirm, *confirm); 765b2fc195eSAndrew Gallatin 766b2fc195eSAndrew Gallatin return ENXIO; 767b2fc195eSAndrew Gallatin } 768b2fc195eSAndrew Gallatin return 0; 769b2fc195eSAndrew Gallatin } 770b2fc195eSAndrew Gallatin 771b2fc195eSAndrew Gallatin static int 7726d87a65dSAndrew Gallatin mxge_update_mac_address(mxge_softc_t *sc) 773b2fc195eSAndrew Gallatin { 7746d87a65dSAndrew Gallatin mxge_cmd_t cmd; 775b2fc195eSAndrew Gallatin uint8_t *addr = sc->mac_addr; 776b2fc195eSAndrew Gallatin int status; 777b2fc195eSAndrew Gallatin 778b2fc195eSAndrew Gallatin 779b2fc195eSAndrew Gallatin cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) 780b2fc195eSAndrew Gallatin | (addr[2] << 8) | addr[3]); 781b2fc195eSAndrew Gallatin 782b2fc195eSAndrew Gallatin cmd.data1 = ((addr[4] << 8) | (addr[5])); 783b2fc195eSAndrew Gallatin 7845e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_SET_MAC_ADDRESS, &cmd); 785b2fc195eSAndrew Gallatin return status; 786b2fc195eSAndrew Gallatin } 787b2fc195eSAndrew Gallatin 788b2fc195eSAndrew Gallatin static int 7896d87a65dSAndrew Gallatin mxge_change_pause(mxge_softc_t *sc, int pause) 790b2fc195eSAndrew Gallatin { 7916d87a65dSAndrew Gallatin mxge_cmd_t cmd; 792b2fc195eSAndrew Gallatin int status; 793b2fc195eSAndrew Gallatin 794b2fc195eSAndrew Gallatin if (pause) 7955e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_FLOW_CONTROL, 796b2fc195eSAndrew Gallatin &cmd); 797b2fc195eSAndrew Gallatin else 7985e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_FLOW_CONTROL, 799b2fc195eSAndrew Gallatin &cmd); 800b2fc195eSAndrew Gallatin 801b2fc195eSAndrew Gallatin if (status) { 802b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set flow control mode\n"); 803b2fc195eSAndrew Gallatin return ENXIO; 804b2fc195eSAndrew Gallatin } 805b2fc195eSAndrew Gallatin sc->pause = pause; 806b2fc195eSAndrew Gallatin return 0; 807b2fc195eSAndrew Gallatin } 808b2fc195eSAndrew Gallatin 809b2fc195eSAndrew Gallatin static void 8106d87a65dSAndrew Gallatin mxge_change_promisc(mxge_softc_t *sc, int promisc) 811b2fc195eSAndrew Gallatin { 8126d87a65dSAndrew Gallatin mxge_cmd_t cmd; 813b2fc195eSAndrew Gallatin int status; 814b2fc195eSAndrew Gallatin 815b2fc195eSAndrew Gallatin if (promisc) 8165e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_ENABLE_PROMISC, 817b2fc195eSAndrew Gallatin &cmd); 818b2fc195eSAndrew Gallatin else 8195e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DISABLE_PROMISC, 820b2fc195eSAndrew Gallatin &cmd); 821b2fc195eSAndrew Gallatin 822b2fc195eSAndrew Gallatin if (status) { 823b2fc195eSAndrew Gallatin device_printf(sc->dev, "Failed to set promisc mode\n"); 824b2fc195eSAndrew Gallatin } 825b2fc195eSAndrew Gallatin } 826b2fc195eSAndrew Gallatin 827b2fc195eSAndrew Gallatin static int 8286d87a65dSAndrew Gallatin mxge_reset(mxge_softc_t *sc) 829b2fc195eSAndrew Gallatin { 830b2fc195eSAndrew Gallatin 8316d87a65dSAndrew Gallatin mxge_cmd_t cmd; 8325e7d8541SAndrew Gallatin mxge_dma_t dmabench_dma; 8335e7d8541SAndrew Gallatin size_t bytes; 8345e7d8541SAndrew Gallatin int status; 835b2fc195eSAndrew Gallatin 836b2fc195eSAndrew Gallatin /* try to send a reset command to the card to see if it 837b2fc195eSAndrew Gallatin is alive */ 838b2fc195eSAndrew Gallatin memset(&cmd, 0, sizeof (cmd)); 8395e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_RESET, &cmd); 840b2fc195eSAndrew Gallatin if (status != 0) { 841b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed reset\n"); 842b2fc195eSAndrew Gallatin return ENXIO; 843b2fc195eSAndrew Gallatin } 844b2fc195eSAndrew Gallatin 845091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 1); 846091feecdSAndrew Gallatin 847b2fc195eSAndrew Gallatin /* Now exchange information about interrupts */ 8485e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry);\ 8495e7d8541SAndrew Gallatin memset(sc->rx_done.entry, 0, bytes); 8505e7d8541SAndrew Gallatin cmd.data0 = (uint32_t)bytes; 8515e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd); 8525e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->rx_done.dma.bus_addr); 8535e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->rx_done.dma.bus_addr); 8545e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_SET_INTRQ_DMA, &cmd); 855b2fc195eSAndrew Gallatin 8566d87a65dSAndrew Gallatin status |= mxge_send_cmd(sc, 8575e7d8541SAndrew Gallatin MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd); 8585e7d8541SAndrew Gallatin 8595e7d8541SAndrew Gallatin 8605e7d8541SAndrew Gallatin sc->intr_coal_delay_ptr = (volatile uint32_t *)(sc->sram + cmd.data0); 8615e7d8541SAndrew Gallatin 8625e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd); 8635e7d8541SAndrew Gallatin sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); 8645e7d8541SAndrew Gallatin 8655e7d8541SAndrew Gallatin 8665e7d8541SAndrew Gallatin status |= mxge_send_cmd(sc, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, 8676d87a65dSAndrew Gallatin &cmd); 8685e7d8541SAndrew Gallatin sc->irq_deassert = (volatile uint32_t *)(sc->sram + cmd.data0); 869b2fc195eSAndrew Gallatin if (status != 0) { 870b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed set interrupt parameters\n"); 871b2fc195eSAndrew Gallatin return status; 872b2fc195eSAndrew Gallatin } 873b2fc195eSAndrew Gallatin 8745e7d8541SAndrew Gallatin 8755e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(sc->intr_coal_delay); 8765e7d8541SAndrew Gallatin 8775e7d8541SAndrew Gallatin 8785e7d8541SAndrew Gallatin /* run a DMA benchmark */ 8795e7d8541SAndrew Gallatin sc->read_dma = sc->write_dma = sc->read_write_dma = 0; 8805e7d8541SAndrew Gallatin status = mxge_dma_alloc(sc, &dmabench_dma, 4096, 4096); 8815e7d8541SAndrew Gallatin if (status) 8825e7d8541SAndrew Gallatin goto dmabench_fail; 8835e7d8541SAndrew Gallatin 8845e7d8541SAndrew Gallatin /* Read DMA */ 8855e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 8865e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 8875e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10000; 8885e7d8541SAndrew Gallatin 8895e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 8905e7d8541SAndrew Gallatin if (status != 0) 8915e7d8541SAndrew Gallatin device_printf(sc->dev, "read dma benchmark failed\n"); 8925e7d8541SAndrew Gallatin else 8935e7d8541SAndrew Gallatin sc->read_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 8945e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 8955e7d8541SAndrew Gallatin 8965e7d8541SAndrew Gallatin /* Write DMA */ 8975e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 8985e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 8995e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x1; 9005e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 9015e7d8541SAndrew Gallatin if (status != 0) 9025e7d8541SAndrew Gallatin device_printf(sc->dev, "write dma benchmark failed\n"); 9035e7d8541SAndrew Gallatin else 9045e7d8541SAndrew Gallatin sc->write_dma = ((cmd.data0>>16) * sc->tx.boundary * 2) / 9055e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 9065e7d8541SAndrew Gallatin /* Read/Write DMA */ 9075e7d8541SAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(dmabench_dma.bus_addr); 9085e7d8541SAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(dmabench_dma.bus_addr); 9095e7d8541SAndrew Gallatin cmd.data2 = sc->tx.boundary * 0x10001; 9105e7d8541SAndrew Gallatin status = mxge_send_cmd(sc, MXGEFW_DMA_TEST, &cmd); 9115e7d8541SAndrew Gallatin if (status != 0) 9125e7d8541SAndrew Gallatin device_printf(sc->dev, "read/write dma benchmark failed\n"); 9135e7d8541SAndrew Gallatin else 9145e7d8541SAndrew Gallatin sc->read_write_dma = 9155e7d8541SAndrew Gallatin ((cmd.data0>>16) * sc->tx.boundary * 2 * 2) / 9165e7d8541SAndrew Gallatin (cmd.data0 & 0xffff); 9175e7d8541SAndrew Gallatin 9185e7d8541SAndrew Gallatin mxge_dma_free(&dmabench_dma); 9195e7d8541SAndrew Gallatin 9205e7d8541SAndrew Gallatin dmabench_fail: 921b2fc195eSAndrew Gallatin /* reset mcp/driver shared state back to 0 */ 9225e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 9235e7d8541SAndrew Gallatin sc->rx_done.idx = 0; 9245e7d8541SAndrew Gallatin sc->rx_done.cnt = 0; 925b2fc195eSAndrew Gallatin sc->tx.req = 0; 926b2fc195eSAndrew Gallatin sc->tx.done = 0; 9275e7d8541SAndrew Gallatin sc->tx.pkt_done = 0; 928b2fc195eSAndrew Gallatin sc->rx_big.cnt = 0; 929b2fc195eSAndrew Gallatin sc->rx_small.cnt = 0; 930b2fc195eSAndrew Gallatin sc->rdma_tags_available = 15; 9316d87a65dSAndrew Gallatin status = mxge_update_mac_address(sc); 9326d87a65dSAndrew Gallatin mxge_change_promisc(sc, 0); 9336d87a65dSAndrew Gallatin mxge_change_pause(sc, sc->pause); 934b2fc195eSAndrew Gallatin return status; 935b2fc195eSAndrew Gallatin } 936b2fc195eSAndrew Gallatin 937b2fc195eSAndrew Gallatin static int 9386d87a65dSAndrew Gallatin mxge_change_intr_coal(SYSCTL_HANDLER_ARGS) 939b2fc195eSAndrew Gallatin { 9406d87a65dSAndrew Gallatin mxge_softc_t *sc; 941b2fc195eSAndrew Gallatin unsigned int intr_coal_delay; 942b2fc195eSAndrew Gallatin int err; 943b2fc195eSAndrew Gallatin 944b2fc195eSAndrew Gallatin sc = arg1; 945b2fc195eSAndrew Gallatin intr_coal_delay = sc->intr_coal_delay; 946b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); 947b2fc195eSAndrew Gallatin if (err != 0) { 948b2fc195eSAndrew Gallatin return err; 949b2fc195eSAndrew Gallatin } 950b2fc195eSAndrew Gallatin if (intr_coal_delay == sc->intr_coal_delay) 951b2fc195eSAndrew Gallatin return 0; 952b2fc195eSAndrew Gallatin 953b2fc195eSAndrew Gallatin if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) 954b2fc195eSAndrew Gallatin return EINVAL; 955b2fc195eSAndrew Gallatin 956b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 9575e7d8541SAndrew Gallatin *sc->intr_coal_delay_ptr = htobe32(intr_coal_delay); 958b2fc195eSAndrew Gallatin sc->intr_coal_delay = intr_coal_delay; 9595e7d8541SAndrew Gallatin 960b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 961b2fc195eSAndrew Gallatin return err; 962b2fc195eSAndrew Gallatin } 963b2fc195eSAndrew Gallatin 964b2fc195eSAndrew Gallatin static int 9656d87a65dSAndrew Gallatin mxge_change_flow_control(SYSCTL_HANDLER_ARGS) 966b2fc195eSAndrew Gallatin { 9676d87a65dSAndrew Gallatin mxge_softc_t *sc; 968b2fc195eSAndrew Gallatin unsigned int enabled; 969b2fc195eSAndrew Gallatin int err; 970b2fc195eSAndrew Gallatin 971b2fc195eSAndrew Gallatin sc = arg1; 972b2fc195eSAndrew Gallatin enabled = sc->pause; 973b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, &enabled, arg2, req); 974b2fc195eSAndrew Gallatin if (err != 0) { 975b2fc195eSAndrew Gallatin return err; 976b2fc195eSAndrew Gallatin } 977b2fc195eSAndrew Gallatin if (enabled == sc->pause) 978b2fc195eSAndrew Gallatin return 0; 979b2fc195eSAndrew Gallatin 980b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 9816d87a65dSAndrew Gallatin err = mxge_change_pause(sc, enabled); 982b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 983b2fc195eSAndrew Gallatin return err; 984b2fc195eSAndrew Gallatin } 985b2fc195eSAndrew Gallatin 986b2fc195eSAndrew Gallatin static int 9876d87a65dSAndrew Gallatin mxge_handle_be32(SYSCTL_HANDLER_ARGS) 988b2fc195eSAndrew Gallatin { 989b2fc195eSAndrew Gallatin int err; 990b2fc195eSAndrew Gallatin 991b2fc195eSAndrew Gallatin if (arg1 == NULL) 992b2fc195eSAndrew Gallatin return EFAULT; 993b2fc195eSAndrew Gallatin arg2 = be32toh(*(int *)arg1); 994b2fc195eSAndrew Gallatin arg1 = NULL; 995b2fc195eSAndrew Gallatin err = sysctl_handle_int(oidp, arg1, arg2, req); 996b2fc195eSAndrew Gallatin 997b2fc195eSAndrew Gallatin return err; 998b2fc195eSAndrew Gallatin } 999b2fc195eSAndrew Gallatin 1000b2fc195eSAndrew Gallatin static void 10016d87a65dSAndrew Gallatin mxge_add_sysctls(mxge_softc_t *sc) 1002b2fc195eSAndrew Gallatin { 1003b2fc195eSAndrew Gallatin struct sysctl_ctx_list *ctx; 1004b2fc195eSAndrew Gallatin struct sysctl_oid_list *children; 10055e7d8541SAndrew Gallatin mcp_irq_data_t *fw; 1006b2fc195eSAndrew Gallatin 1007b2fc195eSAndrew Gallatin ctx = device_get_sysctl_ctx(sc->dev); 1008b2fc195eSAndrew Gallatin children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); 1009b2fc195eSAndrew Gallatin fw = sc->fw_stats; 1010b2fc195eSAndrew Gallatin 10115e7d8541SAndrew Gallatin /* random information */ 10125e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 10135e7d8541SAndrew Gallatin "firmware_version", 10145e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->fw_version, 10155e7d8541SAndrew Gallatin 0, "firmware version"); 10165e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 10175e7d8541SAndrew Gallatin "serial_number", 10185e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->serial_number_string, 10195e7d8541SAndrew Gallatin 0, "serial number"); 10205e7d8541SAndrew Gallatin SYSCTL_ADD_STRING(ctx, children, OID_AUTO, 10215e7d8541SAndrew Gallatin "product_code", 10225e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->product_code_string, 10235e7d8541SAndrew Gallatin 0, "product_code"); 10245e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 10255e7d8541SAndrew Gallatin "tx_boundary", 10265e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.boundary, 10275e7d8541SAndrew Gallatin 0, "tx_boundary"); 10285e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1029091feecdSAndrew Gallatin "write_combine", 1030091feecdSAndrew Gallatin CTLFLAG_RD, &sc->wc, 1031091feecdSAndrew Gallatin 0, "write combining PIO?"); 1032091feecdSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 10335e7d8541SAndrew Gallatin "read_dma_MBs", 10345e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_dma, 10355e7d8541SAndrew Gallatin 0, "DMA Read speed in MB/s"); 10365e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 10375e7d8541SAndrew Gallatin "write_dma_MBs", 10385e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->write_dma, 10395e7d8541SAndrew Gallatin 0, "DMA Write speed in MB/s"); 10405e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 10415e7d8541SAndrew Gallatin "read_write_dma_MBs", 10425e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->read_write_dma, 10435e7d8541SAndrew Gallatin 0, "DMA concurrent Read/Write speed in MB/s"); 10445e7d8541SAndrew Gallatin 10455e7d8541SAndrew Gallatin 10465e7d8541SAndrew Gallatin /* performance related tunables */ 1047b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1048b2fc195eSAndrew Gallatin "intr_coal_delay", 1049b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 10506d87a65dSAndrew Gallatin 0, mxge_change_intr_coal, 1051b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1052b2fc195eSAndrew Gallatin 1053b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1054b2fc195eSAndrew Gallatin "flow_control_enabled", 1055b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RW, sc, 10566d87a65dSAndrew Gallatin 0, mxge_change_flow_control, 1057b2fc195eSAndrew Gallatin "I", "interrupt coalescing delay in usecs"); 1058b2fc195eSAndrew Gallatin 1059b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 10605e7d8541SAndrew Gallatin "deassert_wait", 10615e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_deassert_wait, 10625e7d8541SAndrew Gallatin 0, "Wait for IRQ line to go low in ihandler"); 1063b2fc195eSAndrew Gallatin 1064b2fc195eSAndrew Gallatin /* stats block from firmware is in network byte order. 1065b2fc195eSAndrew Gallatin Need to swap it */ 1066b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1067b2fc195eSAndrew Gallatin "link_up", 1068b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, 10696d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1070b2fc195eSAndrew Gallatin "I", "link up"); 1071b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1072b2fc195eSAndrew Gallatin "rdma_tags_available", 1073b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->rdma_tags_available, 10746d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1075b2fc195eSAndrew Gallatin "I", "rdma_tags_available"); 1076b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1077b2fc195eSAndrew Gallatin "dropped_link_overflow", 1078b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, 10796d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1080b2fc195eSAndrew Gallatin "I", "dropped_link_overflow"); 1081b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1082b2fc195eSAndrew Gallatin "dropped_link_error_or_filtered", 1083b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1084b2fc195eSAndrew Gallatin &fw->dropped_link_error_or_filtered, 10856d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1086b2fc195eSAndrew Gallatin "I", "dropped_link_error_or_filtered"); 1087b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1088b2fc195eSAndrew Gallatin "dropped_runt", 1089b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, 10906d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1091b2fc195eSAndrew Gallatin "I", "dropped_runt"); 1092b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1093b2fc195eSAndrew Gallatin "dropped_overrun", 1094b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, 10956d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1096b2fc195eSAndrew Gallatin "I", "dropped_overrun"); 1097b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1098b2fc195eSAndrew Gallatin "dropped_no_small_buffer", 1099b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, 1100b2fc195eSAndrew Gallatin &fw->dropped_no_small_buffer, 11016d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1102b2fc195eSAndrew Gallatin "I", "dropped_no_small_buffer"); 1103b2fc195eSAndrew Gallatin SYSCTL_ADD_PROC(ctx, children, OID_AUTO, 1104b2fc195eSAndrew Gallatin "dropped_no_big_buffer", 1105b2fc195eSAndrew Gallatin CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, 11066d87a65dSAndrew Gallatin 0, mxge_handle_be32, 1107b2fc195eSAndrew Gallatin "I", "dropped_no_big_buffer"); 1108b2fc195eSAndrew Gallatin 1109b2fc195eSAndrew Gallatin /* host counters exported for debugging */ 1110b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11115e7d8541SAndrew Gallatin "rx_small_cnt", 11125e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_small.cnt, 11135e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 11145e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11155e7d8541SAndrew Gallatin "rx_big_cnt", 11165e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->rx_big.cnt, 11175e7d8541SAndrew Gallatin 0, "rx_small_cnt"); 11185e7d8541SAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1119b2fc195eSAndrew Gallatin "tx_req", 1120b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.req, 1121b2fc195eSAndrew Gallatin 0, "tx_req"); 1122b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 1123b2fc195eSAndrew Gallatin "tx_done", 1124b2fc195eSAndrew Gallatin CTLFLAG_RD, &sc->tx.done, 1125b2fc195eSAndrew Gallatin 0, "tx_done"); 1126b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11275e7d8541SAndrew Gallatin "tx_pkt_done", 11285e7d8541SAndrew Gallatin CTLFLAG_RD, &sc->tx.pkt_done, 11295e7d8541SAndrew Gallatin 0, "tx_done"); 11305e7d8541SAndrew Gallatin 11315e7d8541SAndrew Gallatin /* verbose printing? */ 1132b2fc195eSAndrew Gallatin SYSCTL_ADD_INT(ctx, children, OID_AUTO, 11335e7d8541SAndrew Gallatin "verbose", 11345e7d8541SAndrew Gallatin CTLFLAG_RW, &mxge_verbose, 11355e7d8541SAndrew Gallatin 0, "verbose printing"); 1136b2fc195eSAndrew Gallatin 1137b2fc195eSAndrew Gallatin } 1138b2fc195eSAndrew Gallatin 1139b2fc195eSAndrew Gallatin /* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1140b2fc195eSAndrew Gallatin backwards one at a time and handle ring wraps */ 1141b2fc195eSAndrew Gallatin 1142b2fc195eSAndrew Gallatin static inline void 11436d87a65dSAndrew Gallatin mxge_submit_req_backwards(mxge_tx_buf_t *tx, 1144b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *src, int cnt) 1145b2fc195eSAndrew Gallatin { 1146b2fc195eSAndrew Gallatin int idx, starting_slot; 1147b2fc195eSAndrew Gallatin starting_slot = tx->req; 1148b2fc195eSAndrew Gallatin while (cnt > 1) { 1149b2fc195eSAndrew Gallatin cnt--; 1150b2fc195eSAndrew Gallatin idx = (starting_slot + cnt) & tx->mask; 11516d87a65dSAndrew Gallatin mxge_pio_copy(&tx->lanai[idx], 1152b2fc195eSAndrew Gallatin &src[cnt], sizeof(*src)); 1153b2fc195eSAndrew Gallatin mb(); 1154b2fc195eSAndrew Gallatin } 1155b2fc195eSAndrew Gallatin } 1156b2fc195eSAndrew Gallatin 1157b2fc195eSAndrew Gallatin /* 1158b2fc195eSAndrew Gallatin * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy 1159b2fc195eSAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 1160b2fc195eSAndrew Gallatin * pio handler in the nic. We re-write the first segment's flags 1161b2fc195eSAndrew Gallatin * to mark them valid only after writing the entire chain 1162b2fc195eSAndrew Gallatin */ 1163b2fc195eSAndrew Gallatin 1164b2fc195eSAndrew Gallatin static inline void 11656d87a65dSAndrew Gallatin mxge_submit_req(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, 1166b2fc195eSAndrew Gallatin int cnt) 1167b2fc195eSAndrew Gallatin { 1168b2fc195eSAndrew Gallatin int idx, i; 1169b2fc195eSAndrew Gallatin uint32_t *src_ints; 1170b2fc195eSAndrew Gallatin volatile uint32_t *dst_ints; 1171b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *srcp; 1172b2fc195eSAndrew Gallatin volatile mcp_kreq_ether_send_t *dstp, *dst; 11735e7d8541SAndrew Gallatin uint8_t last_flags; 1174b2fc195eSAndrew Gallatin 1175b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1176b2fc195eSAndrew Gallatin 11775e7d8541SAndrew Gallatin last_flags = src->flags; 11785e7d8541SAndrew Gallatin src->flags = 0; 1179b2fc195eSAndrew Gallatin mb(); 1180b2fc195eSAndrew Gallatin dst = dstp = &tx->lanai[idx]; 1181b2fc195eSAndrew Gallatin srcp = src; 1182b2fc195eSAndrew Gallatin 1183b2fc195eSAndrew Gallatin if ((idx + cnt) < tx->mask) { 1184b2fc195eSAndrew Gallatin for (i = 0; i < (cnt - 1); i += 2) { 11856d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, 2 * sizeof(*src)); 1186b2fc195eSAndrew Gallatin mb(); /* force write every 32 bytes */ 1187b2fc195eSAndrew Gallatin srcp += 2; 1188b2fc195eSAndrew Gallatin dstp += 2; 1189b2fc195eSAndrew Gallatin } 1190b2fc195eSAndrew Gallatin } else { 1191b2fc195eSAndrew Gallatin /* submit all but the first request, and ensure 1192b2fc195eSAndrew Gallatin that it is submitted below */ 11936d87a65dSAndrew Gallatin mxge_submit_req_backwards(tx, src, cnt); 1194b2fc195eSAndrew Gallatin i = 0; 1195b2fc195eSAndrew Gallatin } 1196b2fc195eSAndrew Gallatin if (i < cnt) { 1197b2fc195eSAndrew Gallatin /* submit the first request */ 11986d87a65dSAndrew Gallatin mxge_pio_copy(dstp, srcp, sizeof(*src)); 1199b2fc195eSAndrew Gallatin mb(); /* barrier before setting valid flag */ 1200b2fc195eSAndrew Gallatin } 1201b2fc195eSAndrew Gallatin 1202b2fc195eSAndrew Gallatin /* re-write the last 32-bits with the valid flags */ 12035e7d8541SAndrew Gallatin src->flags = last_flags; 1204b2fc195eSAndrew Gallatin src_ints = (uint32_t *)src; 1205b2fc195eSAndrew Gallatin src_ints+=3; 1206b2fc195eSAndrew Gallatin dst_ints = (volatile uint32_t *)dst; 1207b2fc195eSAndrew Gallatin dst_ints+=3; 1208b2fc195eSAndrew Gallatin *dst_ints = *src_ints; 1209b2fc195eSAndrew Gallatin tx->req += cnt; 1210b2fc195eSAndrew Gallatin mb(); 1211b2fc195eSAndrew Gallatin } 1212b2fc195eSAndrew Gallatin 1213b2fc195eSAndrew Gallatin static inline void 12146d87a65dSAndrew Gallatin mxge_submit_req_wc(mxge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, int cnt) 1215b2fc195eSAndrew Gallatin { 1216b2fc195eSAndrew Gallatin tx->req += cnt; 1217b2fc195eSAndrew Gallatin mb(); 1218b2fc195eSAndrew Gallatin while (cnt >= 4) { 12196d87a65dSAndrew Gallatin mxge_pio_copy((volatile char *)tx->wc_fifo, src, 64); 1220b2fc195eSAndrew Gallatin mb(); 1221b2fc195eSAndrew Gallatin src += 4; 1222b2fc195eSAndrew Gallatin cnt -= 4; 1223b2fc195eSAndrew Gallatin } 1224b2fc195eSAndrew Gallatin if (cnt > 0) { 1225b2fc195eSAndrew Gallatin /* pad it to 64 bytes. The src is 64 bytes bigger than it 1226b2fc195eSAndrew Gallatin needs to be so that we don't overrun it */ 12276d87a65dSAndrew Gallatin mxge_pio_copy(tx->wc_fifo + (cnt<<18), src, 64); 1228b2fc195eSAndrew Gallatin mb(); 1229b2fc195eSAndrew Gallatin } 1230b2fc195eSAndrew Gallatin } 1231b2fc195eSAndrew Gallatin 1232b2fc195eSAndrew Gallatin static void 12336d87a65dSAndrew Gallatin mxge_encap(mxge_softc_t *sc, struct mbuf *m) 1234b2fc195eSAndrew Gallatin { 1235b2fc195eSAndrew Gallatin mcp_kreq_ether_send_t *req; 12365e7d8541SAndrew Gallatin bus_dma_segment_t seg_list[MXGE_MAX_SEND_DESC]; 1237b2fc195eSAndrew Gallatin bus_dma_segment_t *seg; 1238b2fc195eSAndrew Gallatin struct mbuf *m_tmp; 1239b2fc195eSAndrew Gallatin struct ifnet *ifp; 12406d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1241b2fc195eSAndrew Gallatin struct ether_header *eh; 1242b2fc195eSAndrew Gallatin struct ip *ip; 1243b2fc195eSAndrew Gallatin int cnt, cum_len, err, i, idx; 1244b2fc195eSAndrew Gallatin uint16_t flags, pseudo_hdr_offset; 1245b2fc195eSAndrew Gallatin uint8_t cksum_offset; 1246b2fc195eSAndrew Gallatin 1247b2fc195eSAndrew Gallatin 1248b2fc195eSAndrew Gallatin 1249b2fc195eSAndrew Gallatin ifp = sc->ifp; 1250b2fc195eSAndrew Gallatin tx = &sc->tx; 1251b2fc195eSAndrew Gallatin 1252b2fc195eSAndrew Gallatin /* (try to) map the frame for DMA */ 1253b2fc195eSAndrew Gallatin idx = tx->req & tx->mask; 1254b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, 1255b2fc195eSAndrew Gallatin m, seg_list, &cnt, 1256b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1257b2fc195eSAndrew Gallatin if (err == EFBIG) { 1258b2fc195eSAndrew Gallatin /* Too many segments in the chain. Try 1259b2fc195eSAndrew Gallatin to defrag */ 1260b2fc195eSAndrew Gallatin m_tmp = m_defrag(m, M_NOWAIT); 1261b2fc195eSAndrew Gallatin if (m_tmp == NULL) { 1262b2fc195eSAndrew Gallatin goto drop; 1263b2fc195eSAndrew Gallatin } 1264b2fc195eSAndrew Gallatin m = m_tmp; 1265b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(tx->dmat, 1266b2fc195eSAndrew Gallatin tx->info[idx].map, 1267b2fc195eSAndrew Gallatin m, seg_list, &cnt, 1268b2fc195eSAndrew Gallatin BUS_DMA_NOWAIT); 1269b2fc195eSAndrew Gallatin } 1270b2fc195eSAndrew Gallatin if (err != 0) { 1271b2fc195eSAndrew Gallatin device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d\n", 1272b2fc195eSAndrew Gallatin err); 1273b2fc195eSAndrew Gallatin goto drop; 1274b2fc195eSAndrew Gallatin } 1275b2fc195eSAndrew Gallatin bus_dmamap_sync(tx->dmat, tx->info[idx].map, 1276b2fc195eSAndrew Gallatin BUS_DMASYNC_PREWRITE); 12775e7d8541SAndrew Gallatin tx->info[idx].m = m; 1278b2fc195eSAndrew Gallatin 1279b2fc195eSAndrew Gallatin req = tx->req_list; 1280b2fc195eSAndrew Gallatin cksum_offset = 0; 12815e7d8541SAndrew Gallatin pseudo_hdr_offset = 0; 12825e7d8541SAndrew Gallatin flags = MXGEFW_FLAGS_NO_TSO; 1283b2fc195eSAndrew Gallatin 1284b2fc195eSAndrew Gallatin /* checksum offloading? */ 1285b2fc195eSAndrew Gallatin if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { 1286b2fc195eSAndrew Gallatin eh = mtod(m, struct ether_header *); 1287b2fc195eSAndrew Gallatin ip = (struct ip *) (eh + 1); 1288b2fc195eSAndrew Gallatin cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); 1289b2fc195eSAndrew Gallatin pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; 12905e7d8541SAndrew Gallatin pseudo_hdr_offset = htobe16(pseudo_hdr_offset); 1291b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 12925e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_CKSUM; 1293b2fc195eSAndrew Gallatin } 12945e7d8541SAndrew Gallatin if (m->m_pkthdr.len < MXGEFW_SEND_SMALL_SIZE) 12955e7d8541SAndrew Gallatin flags |= MXGEFW_FLAGS_SMALL; 1296b2fc195eSAndrew Gallatin 1297b2fc195eSAndrew Gallatin /* convert segments into a request list */ 1298b2fc195eSAndrew Gallatin cum_len = 0; 1299b2fc195eSAndrew Gallatin seg = seg_list; 13005e7d8541SAndrew Gallatin req->flags = MXGEFW_FLAGS_FIRST; 1301b2fc195eSAndrew Gallatin for (i = 0; i < cnt; i++) { 1302b2fc195eSAndrew Gallatin req->addr_low = 13036d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg->ds_addr)); 1304b2fc195eSAndrew Gallatin req->addr_high = 13056d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg->ds_addr)); 1306b2fc195eSAndrew Gallatin req->length = htobe16(seg->ds_len); 1307b2fc195eSAndrew Gallatin req->cksum_offset = cksum_offset; 1308b2fc195eSAndrew Gallatin if (cksum_offset > seg->ds_len) 1309b2fc195eSAndrew Gallatin cksum_offset -= seg->ds_len; 1310b2fc195eSAndrew Gallatin else 1311b2fc195eSAndrew Gallatin cksum_offset = 0; 13125e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 13135e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 13145e7d8541SAndrew Gallatin req->rdma_count = 1; 13155e7d8541SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD); 1316b2fc195eSAndrew Gallatin cum_len += seg->ds_len; 1317b2fc195eSAndrew Gallatin seg++; 1318b2fc195eSAndrew Gallatin req++; 1319b2fc195eSAndrew Gallatin req->flags = 0; 1320b2fc195eSAndrew Gallatin } 1321b2fc195eSAndrew Gallatin req--; 1322b2fc195eSAndrew Gallatin /* pad runts to 60 bytes */ 1323b2fc195eSAndrew Gallatin if (cum_len < 60) { 1324b2fc195eSAndrew Gallatin req++; 1325b2fc195eSAndrew Gallatin req->addr_low = 13266d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); 1327b2fc195eSAndrew Gallatin req->addr_high = 13286d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); 1329b2fc195eSAndrew Gallatin req->length = htobe16(60 - cum_len); 13305e7d8541SAndrew Gallatin req->cksum_offset = 0; 13315e7d8541SAndrew Gallatin req->pseudo_hdr_offset = pseudo_hdr_offset; 13325e7d8541SAndrew Gallatin req->pad = 0; /* complete solid 16-byte block */ 13335e7d8541SAndrew Gallatin req->rdma_count = 1; 13345e7d8541SAndrew Gallatin req->flags |= flags | ((cum_len & 1) * MXGEFW_FLAGS_ALIGN_ODD); 1335b2fc195eSAndrew Gallatin cnt++; 1336b2fc195eSAndrew Gallatin } 13375e7d8541SAndrew Gallatin 13385e7d8541SAndrew Gallatin tx->req_list[0].rdma_count = cnt; 13395e7d8541SAndrew Gallatin #if 0 13405e7d8541SAndrew Gallatin /* print what the firmware will see */ 13415e7d8541SAndrew Gallatin for (i = 0; i < cnt; i++) { 13425e7d8541SAndrew Gallatin printf("%d: addr: 0x%x 0x%x len:%d pso%d," 13435e7d8541SAndrew Gallatin "cso:%d, flags:0x%x, rdma:%d\n", 13445e7d8541SAndrew Gallatin i, (int)ntohl(tx->req_list[i].addr_high), 13455e7d8541SAndrew Gallatin (int)ntohl(tx->req_list[i].addr_low), 13465e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].length), 13475e7d8541SAndrew Gallatin (int)ntohs(tx->req_list[i].pseudo_hdr_offset), 13485e7d8541SAndrew Gallatin tx->req_list[i].cksum_offset, tx->req_list[i].flags, 13495e7d8541SAndrew Gallatin tx->req_list[i].rdma_count); 13505e7d8541SAndrew Gallatin } 13515e7d8541SAndrew Gallatin printf("--------------\n"); 13525e7d8541SAndrew Gallatin #endif 13535e7d8541SAndrew Gallatin tx->info[((cnt - 1) + tx->req) & tx->mask].flag = 1; 1354b2fc195eSAndrew Gallatin if (tx->wc_fifo == NULL) 13556d87a65dSAndrew Gallatin mxge_submit_req(tx, tx->req_list, cnt); 1356b2fc195eSAndrew Gallatin else 13576d87a65dSAndrew Gallatin mxge_submit_req_wc(tx, tx->req_list, cnt); 1358b2fc195eSAndrew Gallatin return; 1359b2fc195eSAndrew Gallatin 1360b2fc195eSAndrew Gallatin drop: 1361b2fc195eSAndrew Gallatin m_freem(m); 1362b2fc195eSAndrew Gallatin ifp->if_oerrors++; 1363b2fc195eSAndrew Gallatin return; 1364b2fc195eSAndrew Gallatin } 1365b2fc195eSAndrew Gallatin 1366b2fc195eSAndrew Gallatin 13676d914a32SAndrew Gallatin 13686d914a32SAndrew Gallatin 13696d914a32SAndrew Gallatin static inline void 13706d87a65dSAndrew Gallatin mxge_start_locked(mxge_softc_t *sc) 1371b2fc195eSAndrew Gallatin { 1372b2fc195eSAndrew Gallatin struct mbuf *m; 1373b2fc195eSAndrew Gallatin struct ifnet *ifp; 1374b2fc195eSAndrew Gallatin 1375b2fc195eSAndrew Gallatin ifp = sc->ifp; 13766d914a32SAndrew Gallatin while ((sc->tx.mask - (sc->tx.req - sc->tx.done)) 13776d914a32SAndrew Gallatin > MXGE_MAX_SEND_DESC) { 1378b2fc195eSAndrew Gallatin 13796d914a32SAndrew Gallatin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 13806d914a32SAndrew Gallatin if (m == NULL) { 13816d914a32SAndrew Gallatin return; 13826d914a32SAndrew Gallatin } 1383b2fc195eSAndrew Gallatin /* let BPF see it */ 1384b2fc195eSAndrew Gallatin BPF_MTAP(ifp, m); 1385b2fc195eSAndrew Gallatin 1386b2fc195eSAndrew Gallatin /* give it to the nic */ 13876d87a65dSAndrew Gallatin mxge_encap(sc, m); 13886d914a32SAndrew Gallatin } 13896d914a32SAndrew Gallatin /* ran out of transmit slots */ 1390b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1391b2fc195eSAndrew Gallatin } 1392b2fc195eSAndrew Gallatin 1393b2fc195eSAndrew Gallatin static void 13946d87a65dSAndrew Gallatin mxge_start(struct ifnet *ifp) 1395b2fc195eSAndrew Gallatin { 13966d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 1397b2fc195eSAndrew Gallatin 1398b2fc195eSAndrew Gallatin 1399b2fc195eSAndrew Gallatin mtx_lock(&sc->tx_lock); 14006d87a65dSAndrew Gallatin mxge_start_locked(sc); 1401b2fc195eSAndrew Gallatin mtx_unlock(&sc->tx_lock); 1402b2fc195eSAndrew Gallatin } 1403b2fc195eSAndrew Gallatin 14045e7d8541SAndrew Gallatin /* 14055e7d8541SAndrew Gallatin * copy an array of mcp_kreq_ether_recv_t's to the mcp. Copy 14065e7d8541SAndrew Gallatin * at most 32 bytes at a time, so as to avoid involving the software 14075e7d8541SAndrew Gallatin * pio handler in the nic. We re-write the first segment's low 14085e7d8541SAndrew Gallatin * DMA address to mark it valid only after we write the entire chunk 14095e7d8541SAndrew Gallatin * in a burst 14105e7d8541SAndrew Gallatin */ 14115e7d8541SAndrew Gallatin static inline void 14125e7d8541SAndrew Gallatin mxge_submit_8rx(volatile mcp_kreq_ether_recv_t *dst, 14135e7d8541SAndrew Gallatin mcp_kreq_ether_recv_t *src) 14145e7d8541SAndrew Gallatin { 14155e7d8541SAndrew Gallatin uint32_t low; 14165e7d8541SAndrew Gallatin 14175e7d8541SAndrew Gallatin low = src->addr_low; 14185e7d8541SAndrew Gallatin src->addr_low = 0xffffffff; 14195e7d8541SAndrew Gallatin mxge_pio_copy(dst, src, 8 * sizeof (*src)); 14205e7d8541SAndrew Gallatin mb(); 14215e7d8541SAndrew Gallatin dst->addr_low = low; 14225e7d8541SAndrew Gallatin mb(); 14235e7d8541SAndrew Gallatin } 14245e7d8541SAndrew Gallatin 1425b2fc195eSAndrew Gallatin static int 14266d87a65dSAndrew Gallatin mxge_get_buf_small(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1427b2fc195eSAndrew Gallatin { 1428b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1429b2fc195eSAndrew Gallatin struct mbuf *m; 14306d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_small; 1431b2fc195eSAndrew Gallatin int cnt, err; 1432b2fc195eSAndrew Gallatin 1433b2fc195eSAndrew Gallatin m = m_gethdr(M_DONTWAIT, MT_DATA); 1434b2fc195eSAndrew Gallatin if (m == NULL) { 1435b2fc195eSAndrew Gallatin rx->alloc_fail++; 1436b2fc195eSAndrew Gallatin err = ENOBUFS; 1437b2fc195eSAndrew Gallatin goto done; 1438b2fc195eSAndrew Gallatin } 1439b2fc195eSAndrew Gallatin m->m_len = MHLEN; 1440b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1441b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1442b2fc195eSAndrew Gallatin if (err != 0) { 1443b2fc195eSAndrew Gallatin m_free(m); 1444b2fc195eSAndrew Gallatin goto done; 1445b2fc195eSAndrew Gallatin } 1446b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1447b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 14486d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1449b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 14506d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1451b2fc195eSAndrew Gallatin 1452b2fc195eSAndrew Gallatin done: 1453b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 14545e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 14555e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 14565e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 14575e7d8541SAndrew Gallatin else { 1458b2fc195eSAndrew Gallatin mb(); 14595e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 14605e7d8541SAndrew Gallatin } 1461b2fc195eSAndrew Gallatin } 1462b2fc195eSAndrew Gallatin return err; 1463b2fc195eSAndrew Gallatin } 1464b2fc195eSAndrew Gallatin 1465b2fc195eSAndrew Gallatin static int 14666d87a65dSAndrew Gallatin mxge_get_buf_big(mxge_softc_t *sc, bus_dmamap_t map, int idx) 1467b2fc195eSAndrew Gallatin { 1468b2fc195eSAndrew Gallatin bus_dma_segment_t seg; 1469b2fc195eSAndrew Gallatin struct mbuf *m; 14706d87a65dSAndrew Gallatin mxge_rx_buf_t *rx = &sc->rx_big; 1471b2fc195eSAndrew Gallatin int cnt, err; 1472b2fc195eSAndrew Gallatin 1473b2fc195eSAndrew Gallatin m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes); 1474b2fc195eSAndrew Gallatin if (m == NULL) { 1475b2fc195eSAndrew Gallatin rx->alloc_fail++; 1476b2fc195eSAndrew Gallatin err = ENOBUFS; 1477b2fc195eSAndrew Gallatin goto done; 1478b2fc195eSAndrew Gallatin } 1479b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1480b2fc195eSAndrew Gallatin err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, 1481b2fc195eSAndrew Gallatin &seg, &cnt, BUS_DMA_NOWAIT); 1482b2fc195eSAndrew Gallatin if (err != 0) { 1483b2fc195eSAndrew Gallatin m_free(m); 1484b2fc195eSAndrew Gallatin goto done; 1485b2fc195eSAndrew Gallatin } 1486b2fc195eSAndrew Gallatin rx->info[idx].m = m; 1487b2fc195eSAndrew Gallatin rx->shadow[idx].addr_low = 14886d87a65dSAndrew Gallatin htobe32(MXGE_LOWPART_TO_U32(seg.ds_addr)); 1489b2fc195eSAndrew Gallatin rx->shadow[idx].addr_high = 14906d87a65dSAndrew Gallatin htobe32(MXGE_HIGHPART_TO_U32(seg.ds_addr)); 1491b2fc195eSAndrew Gallatin 1492b2fc195eSAndrew Gallatin done: 1493b2fc195eSAndrew Gallatin if ((idx & 7) == 7) { 14945e7d8541SAndrew Gallatin if (rx->wc_fifo == NULL) 14955e7d8541SAndrew Gallatin mxge_submit_8rx(&rx->lanai[idx - 7], 14965e7d8541SAndrew Gallatin &rx->shadow[idx - 7]); 14975e7d8541SAndrew Gallatin else { 1498b2fc195eSAndrew Gallatin mb(); 14995e7d8541SAndrew Gallatin mxge_pio_copy(rx->wc_fifo, &rx->shadow[idx - 7], 64); 15005e7d8541SAndrew Gallatin } 1501b2fc195eSAndrew Gallatin } 1502b2fc195eSAndrew Gallatin return err; 1503b2fc195eSAndrew Gallatin } 1504b2fc195eSAndrew Gallatin 1505b2fc195eSAndrew Gallatin static inline void 15065e7d8541SAndrew Gallatin mxge_rx_csum(struct mbuf *m, int csum) 15075e7d8541SAndrew Gallatin { 15085e7d8541SAndrew Gallatin struct ether_header *eh; 15095e7d8541SAndrew Gallatin struct ip *ip; 15105e7d8541SAndrew Gallatin 15115e7d8541SAndrew Gallatin eh = mtod(m, struct ether_header *); 15125e7d8541SAndrew Gallatin if (__predict_true(eh->ether_type == htons(ETHERTYPE_IP))) { 15135e7d8541SAndrew Gallatin ip = (struct ip *)(eh + 1); 15145e7d8541SAndrew Gallatin if (__predict_true(ip->ip_p == IPPROTO_TCP || 15155e7d8541SAndrew Gallatin ip->ip_p == IPPROTO_UDP)) { 15165e7d8541SAndrew Gallatin m->m_pkthdr.csum_data = csum; 15175e7d8541SAndrew Gallatin m->m_pkthdr.csum_flags = CSUM_DATA_VALID; 15185e7d8541SAndrew Gallatin } 15195e7d8541SAndrew Gallatin } 15205e7d8541SAndrew Gallatin } 15215e7d8541SAndrew Gallatin 15225e7d8541SAndrew Gallatin static inline void 15235e7d8541SAndrew Gallatin mxge_rx_done_big(mxge_softc_t *sc, int len, int csum) 1524b2fc195eSAndrew Gallatin { 1525b2fc195eSAndrew Gallatin struct ifnet *ifp; 1526b2fc195eSAndrew Gallatin struct mbuf *m = 0; /* -Wunitialized */ 1527b2fc195eSAndrew Gallatin struct mbuf *m_prev = 0; /* -Wunitialized */ 1528b2fc195eSAndrew Gallatin struct mbuf *m_head = 0; 1529b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 15306d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1531b2fc195eSAndrew Gallatin int idx; 1532b2fc195eSAndrew Gallatin 1533b2fc195eSAndrew Gallatin 1534b2fc195eSAndrew Gallatin rx = &sc->rx_big; 1535b2fc195eSAndrew Gallatin ifp = sc->ifp; 1536b2fc195eSAndrew Gallatin while (len > 0) { 1537b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1538b2fc195eSAndrew Gallatin rx->cnt++; 1539b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1540b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1541b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 15426d87a65dSAndrew Gallatin if (mxge_get_buf_big(sc, rx->extra_map, idx)) { 1543b2fc195eSAndrew Gallatin goto drop; 1544b2fc195eSAndrew Gallatin } 1545b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1546b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1547b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1548b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1549b2fc195eSAndrew Gallatin 1550b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1551b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1552b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1553b2fc195eSAndrew Gallatin 1554b2fc195eSAndrew Gallatin /* chain multiple segments together */ 1555b2fc195eSAndrew Gallatin if (!m_head) { 1556b2fc195eSAndrew Gallatin m_head = m; 1557b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st bytes so that 1558b2fc195eSAndrew Gallatin * packet is properly aligned */ 15595e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1560b2fc195eSAndrew Gallatin m->m_pkthdr.len = len; 15615e7d8541SAndrew Gallatin m->m_len = sc->big_bytes - MXGEFW_PAD; 1562b2fc195eSAndrew Gallatin } else { 1563b2fc195eSAndrew Gallatin m->m_len = sc->big_bytes; 1564b2fc195eSAndrew Gallatin m->m_flags &= ~M_PKTHDR; 1565b2fc195eSAndrew Gallatin m_prev->m_next = m; 1566b2fc195eSAndrew Gallatin } 1567b2fc195eSAndrew Gallatin len -= m->m_len; 1568b2fc195eSAndrew Gallatin m_prev = m; 1569b2fc195eSAndrew Gallatin } 1570b2fc195eSAndrew Gallatin 1571b2fc195eSAndrew Gallatin /* trim trailing garbage from the last mbuf in the chain. If 1572b2fc195eSAndrew Gallatin * there is any garbage, len will be negative */ 1573b2fc195eSAndrew Gallatin m->m_len += len; 1574b2fc195eSAndrew Gallatin 1575b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 15765e7d8541SAndrew Gallatin if (sc->csum_flag) 15775e7d8541SAndrew Gallatin mxge_rx_csum(m_head, csum); 1578b2fc195eSAndrew Gallatin 1579b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1580b2fc195eSAndrew Gallatin m_head->m_pkthdr.rcvif = ifp; 1581b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1582b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m_head); 1583b2fc195eSAndrew Gallatin return; 1584b2fc195eSAndrew Gallatin 1585b2fc195eSAndrew Gallatin drop: 1586b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf(s) are re-cycled by running 1587b2fc195eSAndrew Gallatin every slot through the allocator */ 1588b2fc195eSAndrew Gallatin if (m_head) { 1589b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1590b2fc195eSAndrew Gallatin m_freem(m_head); 1591b2fc195eSAndrew Gallatin } else { 15925e7d8541SAndrew Gallatin len -= (sc->big_bytes + MXGEFW_PAD); 1593b2fc195eSAndrew Gallatin } 1594b2fc195eSAndrew Gallatin while ((int)len > 0) { 1595b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1596b2fc195eSAndrew Gallatin rx->cnt++; 1597b2fc195eSAndrew Gallatin m = rx->info[idx].m; 15986d87a65dSAndrew Gallatin if (0 == (mxge_get_buf_big(sc, rx->extra_map, idx))) { 1599b2fc195eSAndrew Gallatin m_freem(m); 1600b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1601b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1602b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, 1603b2fc195eSAndrew Gallatin BUS_DMASYNC_POSTREAD); 1604b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1605b2fc195eSAndrew Gallatin 1606b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1607b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1608b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1609b2fc195eSAndrew Gallatin } 1610b2fc195eSAndrew Gallatin len -= sc->big_bytes; 1611b2fc195eSAndrew Gallatin } 1612b2fc195eSAndrew Gallatin 1613b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1614b2fc195eSAndrew Gallatin 1615b2fc195eSAndrew Gallatin } 1616b2fc195eSAndrew Gallatin 1617b2fc195eSAndrew Gallatin static inline void 16185e7d8541SAndrew Gallatin mxge_rx_done_small(mxge_softc_t *sc, uint32_t len, uint32_t csum) 1619b2fc195eSAndrew Gallatin { 1620b2fc195eSAndrew Gallatin struct ifnet *ifp; 1621b2fc195eSAndrew Gallatin struct mbuf *m; 16226d87a65dSAndrew Gallatin mxge_rx_buf_t *rx; 1623b2fc195eSAndrew Gallatin bus_dmamap_t old_map; 1624b2fc195eSAndrew Gallatin int idx; 1625b2fc195eSAndrew Gallatin 1626b2fc195eSAndrew Gallatin ifp = sc->ifp; 1627b2fc195eSAndrew Gallatin rx = &sc->rx_small; 1628b2fc195eSAndrew Gallatin idx = rx->cnt & rx->mask; 1629b2fc195eSAndrew Gallatin rx->cnt++; 1630b2fc195eSAndrew Gallatin /* save a pointer to the received mbuf */ 1631b2fc195eSAndrew Gallatin m = rx->info[idx].m; 1632b2fc195eSAndrew Gallatin /* try to replace the received mbuf */ 16336d87a65dSAndrew Gallatin if (mxge_get_buf_small(sc, rx->extra_map, idx)) { 1634b2fc195eSAndrew Gallatin /* drop the frame -- the old mbuf is re-cycled */ 1635b2fc195eSAndrew Gallatin ifp->if_ierrors++; 1636b2fc195eSAndrew Gallatin return; 1637b2fc195eSAndrew Gallatin } 1638b2fc195eSAndrew Gallatin 1639b2fc195eSAndrew Gallatin /* unmap the received buffer */ 1640b2fc195eSAndrew Gallatin old_map = rx->info[idx].map; 1641b2fc195eSAndrew Gallatin bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); 1642b2fc195eSAndrew Gallatin bus_dmamap_unload(rx->dmat, old_map); 1643b2fc195eSAndrew Gallatin 1644b2fc195eSAndrew Gallatin /* swap the bus_dmamap_t's */ 1645b2fc195eSAndrew Gallatin rx->info[idx].map = rx->extra_map; 1646b2fc195eSAndrew Gallatin rx->extra_map = old_map; 1647b2fc195eSAndrew Gallatin 1648b2fc195eSAndrew Gallatin /* mcp implicitly skips 1st 2 bytes so that packet is properly 1649b2fc195eSAndrew Gallatin * aligned */ 16505e7d8541SAndrew Gallatin m->m_data += MXGEFW_PAD; 1651b2fc195eSAndrew Gallatin 1652b2fc195eSAndrew Gallatin /* if the checksum is valid, mark it in the mbuf header */ 16535e7d8541SAndrew Gallatin if (sc->csum_flag) 16545e7d8541SAndrew Gallatin mxge_rx_csum(m, csum); 1655b2fc195eSAndrew Gallatin 1656b2fc195eSAndrew Gallatin /* pass the frame up the stack */ 1657b2fc195eSAndrew Gallatin m->m_pkthdr.rcvif = ifp; 1658b2fc195eSAndrew Gallatin m->m_len = m->m_pkthdr.len = len; 1659b2fc195eSAndrew Gallatin ifp->if_ipackets++; 1660b2fc195eSAndrew Gallatin (*ifp->if_input)(ifp, m); 1661b2fc195eSAndrew Gallatin } 1662b2fc195eSAndrew Gallatin 1663b2fc195eSAndrew Gallatin static inline void 16645e7d8541SAndrew Gallatin mxge_clean_rx_done(mxge_softc_t *sc) 16655e7d8541SAndrew Gallatin { 16665e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 16675e7d8541SAndrew Gallatin int limit = 0; 16685e7d8541SAndrew Gallatin uint16_t length; 16695e7d8541SAndrew Gallatin uint16_t checksum; 16705e7d8541SAndrew Gallatin 16715e7d8541SAndrew Gallatin 16725e7d8541SAndrew Gallatin while (rx_done->entry[rx_done->idx].length != 0) { 16735e7d8541SAndrew Gallatin length = ntohs(rx_done->entry[rx_done->idx].length); 16745e7d8541SAndrew Gallatin rx_done->entry[rx_done->idx].length = 0; 16755e7d8541SAndrew Gallatin checksum = ntohs(rx_done->entry[rx_done->idx].checksum); 16765e7d8541SAndrew Gallatin if (length <= MHLEN) 16775e7d8541SAndrew Gallatin mxge_rx_done_small(sc, length, checksum); 16785e7d8541SAndrew Gallatin else 16795e7d8541SAndrew Gallatin mxge_rx_done_big(sc, length, checksum); 16805e7d8541SAndrew Gallatin rx_done->cnt++; 16815e7d8541SAndrew Gallatin rx_done->idx = rx_done->cnt & (mxge_max_intr_slots - 1); 16825e7d8541SAndrew Gallatin 16835e7d8541SAndrew Gallatin /* limit potential for livelock */ 16845e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * mxge_max_intr_slots)) 16855e7d8541SAndrew Gallatin break; 16865e7d8541SAndrew Gallatin 16875e7d8541SAndrew Gallatin } 16885e7d8541SAndrew Gallatin } 16895e7d8541SAndrew Gallatin 16905e7d8541SAndrew Gallatin 16915e7d8541SAndrew Gallatin static inline void 16926d87a65dSAndrew Gallatin mxge_tx_done(mxge_softc_t *sc, uint32_t mcp_idx) 1693b2fc195eSAndrew Gallatin { 1694b2fc195eSAndrew Gallatin struct ifnet *ifp; 16956d87a65dSAndrew Gallatin mxge_tx_buf_t *tx; 1696b2fc195eSAndrew Gallatin struct mbuf *m; 1697b2fc195eSAndrew Gallatin bus_dmamap_t map; 16985e7d8541SAndrew Gallatin int idx, limit; 1699b2fc195eSAndrew Gallatin 17005e7d8541SAndrew Gallatin limit = 0; 1701b2fc195eSAndrew Gallatin tx = &sc->tx; 1702b2fc195eSAndrew Gallatin ifp = sc->ifp; 17035e7d8541SAndrew Gallatin while (tx->pkt_done != mcp_idx) { 1704b2fc195eSAndrew Gallatin idx = tx->done & tx->mask; 1705b2fc195eSAndrew Gallatin tx->done++; 1706b2fc195eSAndrew Gallatin m = tx->info[idx].m; 1707b2fc195eSAndrew Gallatin /* mbuf and DMA map only attached to the first 1708b2fc195eSAndrew Gallatin segment per-mbuf */ 1709b2fc195eSAndrew Gallatin if (m != NULL) { 1710b2fc195eSAndrew Gallatin ifp->if_opackets++; 1711b2fc195eSAndrew Gallatin tx->info[idx].m = NULL; 1712b2fc195eSAndrew Gallatin map = tx->info[idx].map; 1713b2fc195eSAndrew Gallatin bus_dmamap_unload(tx->dmat, map); 1714b2fc195eSAndrew Gallatin m_freem(m); 1715b2fc195eSAndrew Gallatin } 17165e7d8541SAndrew Gallatin if (tx->info[idx].flag) { 17175e7d8541SAndrew Gallatin tx->info[idx].flag = 0; 17185e7d8541SAndrew Gallatin tx->pkt_done++; 17195e7d8541SAndrew Gallatin } 17205e7d8541SAndrew Gallatin /* limit potential for livelock by only handling 17215e7d8541SAndrew Gallatin 2 full tx rings per call */ 17225e7d8541SAndrew Gallatin if (__predict_false(++limit > 2 * tx->mask)) 17235e7d8541SAndrew Gallatin break; 1724b2fc195eSAndrew Gallatin } 1725b2fc195eSAndrew Gallatin 1726b2fc195eSAndrew Gallatin /* If we have space, clear IFF_OACTIVE to tell the stack that 1727b2fc195eSAndrew Gallatin its OK to send packets */ 1728b2fc195eSAndrew Gallatin 1729b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_OACTIVE && 1730b2fc195eSAndrew Gallatin tx->req - tx->done < (tx->mask + 1)/4) { 1731b2fc195eSAndrew Gallatin mtx_lock(&sc->tx_lock); 1732b2fc195eSAndrew Gallatin ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 17336d87a65dSAndrew Gallatin mxge_start_locked(sc); 1734b2fc195eSAndrew Gallatin mtx_unlock(&sc->tx_lock); 1735b2fc195eSAndrew Gallatin } 1736b2fc195eSAndrew Gallatin } 1737b2fc195eSAndrew Gallatin 1738b2fc195eSAndrew Gallatin static void 17396d87a65dSAndrew Gallatin mxge_intr(void *arg) 1740b2fc195eSAndrew Gallatin { 17416d87a65dSAndrew Gallatin mxge_softc_t *sc = arg; 17425e7d8541SAndrew Gallatin mcp_irq_data_t *stats = sc->fw_stats; 17435e7d8541SAndrew Gallatin mxge_tx_buf_t *tx = &sc->tx; 17445e7d8541SAndrew Gallatin mxge_rx_done_t *rx_done = &sc->rx_done; 17455e7d8541SAndrew Gallatin uint32_t send_done_count; 17465e7d8541SAndrew Gallatin uint8_t valid; 1747b2fc195eSAndrew Gallatin 1748b2fc195eSAndrew Gallatin 17495e7d8541SAndrew Gallatin /* make sure the DMA has finished */ 17505e7d8541SAndrew Gallatin if (!stats->valid) { 17515e7d8541SAndrew Gallatin return; 1752b2fc195eSAndrew Gallatin } 17535e7d8541SAndrew Gallatin valid = stats->valid; 1754b2fc195eSAndrew Gallatin 17555e7d8541SAndrew Gallatin /* lower legacy IRQ */ 17565e7d8541SAndrew Gallatin *sc->irq_deassert = 0; 1757b2fc195eSAndrew Gallatin mb(); 17585e7d8541SAndrew Gallatin if (!mxge_deassert_wait) 17595e7d8541SAndrew Gallatin /* don't wait for conf. that irq is low */ 17605e7d8541SAndrew Gallatin stats->valid = 0; 17615e7d8541SAndrew Gallatin do { 17625e7d8541SAndrew Gallatin /* check for transmit completes and receives */ 17635e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 17645e7d8541SAndrew Gallatin while ((send_done_count != tx->pkt_done) || 17655e7d8541SAndrew Gallatin (rx_done->entry[rx_done->idx].length != 0)) { 17665e7d8541SAndrew Gallatin mxge_tx_done(sc, (int)send_done_count); 17675e7d8541SAndrew Gallatin mxge_clean_rx_done(sc); 17685e7d8541SAndrew Gallatin send_done_count = be32toh(stats->send_done_count); 1769b2fc195eSAndrew Gallatin } 17705e7d8541SAndrew Gallatin } while (*((volatile uint8_t *) &stats->valid)); 1771b2fc195eSAndrew Gallatin 17725e7d8541SAndrew Gallatin if (__predict_false(stats->stats_updated)) { 17735e7d8541SAndrew Gallatin if (sc->link_state != stats->link_up) { 17745e7d8541SAndrew Gallatin sc->link_state = stats->link_up; 1775b2fc195eSAndrew Gallatin if (sc->link_state) { 17765e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_UP); 17775e7d8541SAndrew Gallatin if (mxge_verbose) 17785e7d8541SAndrew Gallatin device_printf(sc->dev, "link up\n"); 1779b2fc195eSAndrew Gallatin } else { 17805e7d8541SAndrew Gallatin if_link_state_change(sc->ifp, LINK_STATE_DOWN); 17815e7d8541SAndrew Gallatin if (mxge_verbose) 17825e7d8541SAndrew Gallatin device_printf(sc->dev, "link down\n"); 1783b2fc195eSAndrew Gallatin } 1784b2fc195eSAndrew Gallatin } 1785b2fc195eSAndrew Gallatin if (sc->rdma_tags_available != 1786b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available)) { 1787b2fc195eSAndrew Gallatin sc->rdma_tags_available = 1788b2fc195eSAndrew Gallatin be32toh(sc->fw_stats->rdma_tags_available); 17895e7d8541SAndrew Gallatin device_printf(sc->dev, "RDMA timed out! %d tags " 17905e7d8541SAndrew Gallatin "left\n", sc->rdma_tags_available); 17915e7d8541SAndrew Gallatin } 17925e7d8541SAndrew Gallatin sc->down_cnt += stats->link_down; 1793b2fc195eSAndrew Gallatin } 1794b2fc195eSAndrew Gallatin 17955e7d8541SAndrew Gallatin /* check to see if we have rx token to pass back */ 17965e7d8541SAndrew Gallatin if (valid & 0x1) 17975e7d8541SAndrew Gallatin *sc->irq_claim = be32toh(3); 17985e7d8541SAndrew Gallatin *(sc->irq_claim + 1) = be32toh(3); 1799b2fc195eSAndrew Gallatin } 1800b2fc195eSAndrew Gallatin 1801b2fc195eSAndrew Gallatin static void 18026d87a65dSAndrew Gallatin mxge_watchdog(struct ifnet *ifp) 1803b2fc195eSAndrew Gallatin { 1804b2fc195eSAndrew Gallatin printf("%s called\n", __FUNCTION__); 1805b2fc195eSAndrew Gallatin } 1806b2fc195eSAndrew Gallatin 1807b2fc195eSAndrew Gallatin static void 18086d87a65dSAndrew Gallatin mxge_init(void *arg) 1809b2fc195eSAndrew Gallatin { 1810b2fc195eSAndrew Gallatin } 1811b2fc195eSAndrew Gallatin 1812b2fc195eSAndrew Gallatin 1813b2fc195eSAndrew Gallatin 1814b2fc195eSAndrew Gallatin static void 18156d87a65dSAndrew Gallatin mxge_free_mbufs(mxge_softc_t *sc) 1816b2fc195eSAndrew Gallatin { 1817b2fc195eSAndrew Gallatin int i; 1818b2fc195eSAndrew Gallatin 1819b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 1820b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 1821b2fc195eSAndrew Gallatin continue; 1822b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 1823b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 1824b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 1825b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 1826b2fc195eSAndrew Gallatin } 1827b2fc195eSAndrew Gallatin 1828b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 1829b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].m == NULL) 1830b2fc195eSAndrew Gallatin continue; 1831b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->rx_big.dmat, 1832b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 1833b2fc195eSAndrew Gallatin m_freem(sc->rx_big.info[i].m); 1834b2fc195eSAndrew Gallatin sc->rx_big.info[i].m = NULL; 1835b2fc195eSAndrew Gallatin } 1836b2fc195eSAndrew Gallatin 1837b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 1838b2fc195eSAndrew Gallatin if (sc->tx.info[i].m == NULL) 1839b2fc195eSAndrew Gallatin continue; 1840b2fc195eSAndrew Gallatin bus_dmamap_unload(sc->tx.dmat, 1841b2fc195eSAndrew Gallatin sc->tx.info[i].map); 1842b2fc195eSAndrew Gallatin m_freem(sc->tx.info[i].m); 1843b2fc195eSAndrew Gallatin sc->tx.info[i].m = NULL; 1844b2fc195eSAndrew Gallatin } 1845b2fc195eSAndrew Gallatin } 1846b2fc195eSAndrew Gallatin 1847b2fc195eSAndrew Gallatin static void 18486d87a65dSAndrew Gallatin mxge_free_rings(mxge_softc_t *sc) 1849b2fc195eSAndrew Gallatin { 1850b2fc195eSAndrew Gallatin int i; 1851b2fc195eSAndrew Gallatin 1852b2fc195eSAndrew Gallatin if (sc->tx.req_bytes != NULL) { 1853b2fc195eSAndrew Gallatin free(sc->tx.req_bytes, M_DEVBUF); 1854b2fc195eSAndrew Gallatin } 1855b2fc195eSAndrew Gallatin if (sc->rx_small.shadow != NULL) 1856b2fc195eSAndrew Gallatin free(sc->rx_small.shadow, M_DEVBUF); 1857b2fc195eSAndrew Gallatin if (sc->rx_big.shadow != NULL) 1858b2fc195eSAndrew Gallatin free(sc->rx_big.shadow, M_DEVBUF); 1859b2fc195eSAndrew Gallatin if (sc->tx.info != NULL) { 1860b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 1861b2fc195eSAndrew Gallatin if (sc->tx.info[i].map != NULL) 1862b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->tx.dmat, 1863b2fc195eSAndrew Gallatin sc->tx.info[i].map); 1864b2fc195eSAndrew Gallatin } 1865b2fc195eSAndrew Gallatin free(sc->tx.info, M_DEVBUF); 1866b2fc195eSAndrew Gallatin } 1867b2fc195eSAndrew Gallatin if (sc->rx_small.info != NULL) { 1868b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 1869b2fc195eSAndrew Gallatin if (sc->rx_small.info[i].map != NULL) 1870b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 1871b2fc195eSAndrew Gallatin sc->rx_small.info[i].map); 1872b2fc195eSAndrew Gallatin } 1873b2fc195eSAndrew Gallatin free(sc->rx_small.info, M_DEVBUF); 1874b2fc195eSAndrew Gallatin } 1875b2fc195eSAndrew Gallatin if (sc->rx_big.info != NULL) { 1876b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 1877b2fc195eSAndrew Gallatin if (sc->rx_big.info[i].map != NULL) 1878b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 1879b2fc195eSAndrew Gallatin sc->rx_big.info[i].map); 1880b2fc195eSAndrew Gallatin } 1881b2fc195eSAndrew Gallatin free(sc->rx_big.info, M_DEVBUF); 1882b2fc195eSAndrew Gallatin } 1883b2fc195eSAndrew Gallatin if (sc->rx_big.extra_map != NULL) 1884b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_big.dmat, 1885b2fc195eSAndrew Gallatin sc->rx_big.extra_map); 1886b2fc195eSAndrew Gallatin if (sc->rx_small.extra_map != NULL) 1887b2fc195eSAndrew Gallatin bus_dmamap_destroy(sc->rx_small.dmat, 1888b2fc195eSAndrew Gallatin sc->rx_small.extra_map); 1889b2fc195eSAndrew Gallatin if (sc->tx.dmat != NULL) 1890b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->tx.dmat); 1891b2fc195eSAndrew Gallatin if (sc->rx_small.dmat != NULL) 1892b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_small.dmat); 1893b2fc195eSAndrew Gallatin if (sc->rx_big.dmat != NULL) 1894b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->rx_big.dmat); 1895b2fc195eSAndrew Gallatin } 1896b2fc195eSAndrew Gallatin 1897b2fc195eSAndrew Gallatin static int 18986d87a65dSAndrew Gallatin mxge_alloc_rings(mxge_softc_t *sc) 1899b2fc195eSAndrew Gallatin { 19006d87a65dSAndrew Gallatin mxge_cmd_t cmd; 1901b2fc195eSAndrew Gallatin int tx_ring_size, rx_ring_size; 1902b2fc195eSAndrew Gallatin int tx_ring_entries, rx_ring_entries; 1903b2fc195eSAndrew Gallatin int i, err; 1904b2fc195eSAndrew Gallatin unsigned long bytes; 1905b2fc195eSAndrew Gallatin 1906b2fc195eSAndrew Gallatin /* get ring sizes */ 19075e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd); 1908b2fc195eSAndrew Gallatin tx_ring_size = cmd.data0; 19095e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd); 1910b2fc195eSAndrew Gallatin if (err != 0) { 1911b2fc195eSAndrew Gallatin device_printf(sc->dev, "Cannot determine ring sizes\n"); 1912b2fc195eSAndrew Gallatin goto abort_with_nothing; 1913b2fc195eSAndrew Gallatin } 1914b2fc195eSAndrew Gallatin 1915b2fc195eSAndrew Gallatin rx_ring_size = cmd.data0; 1916b2fc195eSAndrew Gallatin 1917b2fc195eSAndrew Gallatin tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); 1918b2fc195eSAndrew Gallatin rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); 1919b2fc195eSAndrew Gallatin sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; 192076bb9c5eSAndrew Gallatin IFQ_SET_MAXLEN(&sc->ifp->if_snd, tx_ring_entries - 1); 192176bb9c5eSAndrew Gallatin IFQ_SET_READY(&sc->ifp->if_snd); 1922b2fc195eSAndrew Gallatin 1923b2fc195eSAndrew Gallatin sc->tx.mask = tx_ring_entries - 1; 1924b2fc195eSAndrew Gallatin sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; 1925b2fc195eSAndrew Gallatin 1926b2fc195eSAndrew Gallatin err = ENOMEM; 1927b2fc195eSAndrew Gallatin 1928b2fc195eSAndrew Gallatin /* allocate the tx request copy block */ 1929b2fc195eSAndrew Gallatin bytes = 8 + 19305e7d8541SAndrew Gallatin sizeof (*sc->tx.req_list) * (MXGE_MAX_SEND_DESC + 4); 1931b2fc195eSAndrew Gallatin sc->tx.req_bytes = malloc(bytes, M_DEVBUF, M_WAITOK); 1932b2fc195eSAndrew Gallatin if (sc->tx.req_bytes == NULL) 1933b2fc195eSAndrew Gallatin goto abort_with_nothing; 1934b2fc195eSAndrew Gallatin /* ensure req_list entries are aligned to 8 bytes */ 1935b2fc195eSAndrew Gallatin sc->tx.req_list = (mcp_kreq_ether_send_t *) 1936b2fc195eSAndrew Gallatin ((unsigned long)(sc->tx.req_bytes + 7) & ~7UL); 1937b2fc195eSAndrew Gallatin 1938b2fc195eSAndrew Gallatin /* allocate the rx shadow rings */ 1939b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); 1940b2fc195eSAndrew Gallatin sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 1941b2fc195eSAndrew Gallatin if (sc->rx_small.shadow == NULL) 1942b2fc195eSAndrew Gallatin goto abort_with_alloc; 1943b2fc195eSAndrew Gallatin 1944b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); 1945b2fc195eSAndrew Gallatin sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 1946b2fc195eSAndrew Gallatin if (sc->rx_big.shadow == NULL) 1947b2fc195eSAndrew Gallatin goto abort_with_alloc; 1948b2fc195eSAndrew Gallatin 1949b2fc195eSAndrew Gallatin /* allocate the host info rings */ 1950b2fc195eSAndrew Gallatin bytes = tx_ring_entries * sizeof (*sc->tx.info); 1951b2fc195eSAndrew Gallatin sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 1952b2fc195eSAndrew Gallatin if (sc->tx.info == NULL) 1953b2fc195eSAndrew Gallatin goto abort_with_alloc; 1954b2fc195eSAndrew Gallatin 1955b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_small.info); 1956b2fc195eSAndrew Gallatin sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 1957b2fc195eSAndrew Gallatin if (sc->rx_small.info == NULL) 1958b2fc195eSAndrew Gallatin goto abort_with_alloc; 1959b2fc195eSAndrew Gallatin 1960b2fc195eSAndrew Gallatin bytes = rx_ring_entries * sizeof (*sc->rx_big.info); 1961b2fc195eSAndrew Gallatin sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); 1962b2fc195eSAndrew Gallatin if (sc->rx_big.info == NULL) 1963b2fc195eSAndrew Gallatin goto abort_with_alloc; 1964b2fc195eSAndrew Gallatin 1965b2fc195eSAndrew Gallatin /* allocate the busdma resources */ 1966b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 1967b2fc195eSAndrew Gallatin 1, /* alignment */ 1968b2fc195eSAndrew Gallatin sc->tx.boundary, /* boundary */ 1969b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 1970b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 1971b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 19726d87a65dSAndrew Gallatin MXGE_MAX_ETHER_MTU, /* maxsize */ 19735e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 1974b2fc195eSAndrew Gallatin sc->tx.boundary, /* maxsegsize */ 1975b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 1976b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 1977b2fc195eSAndrew Gallatin &sc->tx.dmat); /* tag */ 1978b2fc195eSAndrew Gallatin 1979b2fc195eSAndrew Gallatin if (err != 0) { 1980b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating tx dmat\n", 1981b2fc195eSAndrew Gallatin err); 1982b2fc195eSAndrew Gallatin goto abort_with_alloc; 1983b2fc195eSAndrew Gallatin } 1984b2fc195eSAndrew Gallatin 1985b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 1986b2fc195eSAndrew Gallatin 1, /* alignment */ 1987b2fc195eSAndrew Gallatin 4096, /* boundary */ 1988b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 1989b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 1990b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 1991b2fc195eSAndrew Gallatin MHLEN, /* maxsize */ 1992b2fc195eSAndrew Gallatin 1, /* num segs */ 1993b2fc195eSAndrew Gallatin MHLEN, /* maxsegsize */ 1994b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 1995b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 1996b2fc195eSAndrew Gallatin &sc->rx_small.dmat); /* tag */ 1997b2fc195eSAndrew Gallatin if (err != 0) { 1998b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_small dmat\n", 1999b2fc195eSAndrew Gallatin err); 2000b2fc195eSAndrew Gallatin goto abort_with_alloc; 2001b2fc195eSAndrew Gallatin } 2002b2fc195eSAndrew Gallatin 2003b2fc195eSAndrew Gallatin err = bus_dma_tag_create(sc->parent_dmat, /* parent */ 2004b2fc195eSAndrew Gallatin 1, /* alignment */ 2005b2fc195eSAndrew Gallatin 4096, /* boundary */ 2006b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2007b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2008b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 2009b2fc195eSAndrew Gallatin 4096, /* maxsize */ 2010b2fc195eSAndrew Gallatin 1, /* num segs */ 2011b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2012b2fc195eSAndrew Gallatin BUS_DMA_ALLOCNOW, /* flags */ 2013b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2014b2fc195eSAndrew Gallatin &sc->rx_big.dmat); /* tag */ 2015b2fc195eSAndrew Gallatin if (err != 0) { 2016b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating rx_big dmat\n", 2017b2fc195eSAndrew Gallatin err); 2018b2fc195eSAndrew Gallatin goto abort_with_alloc; 2019b2fc195eSAndrew Gallatin } 2020b2fc195eSAndrew Gallatin 2021b2fc195eSAndrew Gallatin /* now use these tags to setup dmamaps for each slot 2022b2fc195eSAndrew Gallatin in each ring */ 2023b2fc195eSAndrew Gallatin for (i = 0; i <= sc->tx.mask; i++) { 2024b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->tx.dmat, 0, 2025b2fc195eSAndrew Gallatin &sc->tx.info[i].map); 2026b2fc195eSAndrew Gallatin if (err != 0) { 2027b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d tx dmamap\n", 2028b2fc195eSAndrew Gallatin err); 2029b2fc195eSAndrew Gallatin goto abort_with_alloc; 2030b2fc195eSAndrew Gallatin } 2031b2fc195eSAndrew Gallatin } 2032b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2033b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2034b2fc195eSAndrew Gallatin &sc->rx_small.info[i].map); 2035b2fc195eSAndrew Gallatin if (err != 0) { 2036b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_small dmamap\n", 2037b2fc195eSAndrew Gallatin err); 2038b2fc195eSAndrew Gallatin goto abort_with_alloc; 2039b2fc195eSAndrew Gallatin } 2040b2fc195eSAndrew Gallatin } 2041b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_small.dmat, 0, 2042b2fc195eSAndrew Gallatin &sc->rx_small.extra_map); 2043b2fc195eSAndrew Gallatin if (err != 0) { 2044b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_small dmamap\n", 2045b2fc195eSAndrew Gallatin err); 2046b2fc195eSAndrew Gallatin goto abort_with_alloc; 2047b2fc195eSAndrew Gallatin } 2048b2fc195eSAndrew Gallatin 2049b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2050b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2051b2fc195eSAndrew Gallatin &sc->rx_big.info[i].map); 2052b2fc195eSAndrew Gallatin if (err != 0) { 2053b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d rx_big dmamap\n", 2054b2fc195eSAndrew Gallatin err); 2055b2fc195eSAndrew Gallatin goto abort_with_alloc; 2056b2fc195eSAndrew Gallatin } 2057b2fc195eSAndrew Gallatin } 2058b2fc195eSAndrew Gallatin err = bus_dmamap_create(sc->rx_big.dmat, 0, 2059b2fc195eSAndrew Gallatin &sc->rx_big.extra_map); 2060b2fc195eSAndrew Gallatin if (err != 0) { 2061b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d extra rx_big dmamap\n", 2062b2fc195eSAndrew Gallatin err); 2063b2fc195eSAndrew Gallatin goto abort_with_alloc; 2064b2fc195eSAndrew Gallatin } 2065b2fc195eSAndrew Gallatin return 0; 2066b2fc195eSAndrew Gallatin 2067b2fc195eSAndrew Gallatin abort_with_alloc: 20686d87a65dSAndrew Gallatin mxge_free_rings(sc); 2069b2fc195eSAndrew Gallatin 2070b2fc195eSAndrew Gallatin abort_with_nothing: 2071b2fc195eSAndrew Gallatin return err; 2072b2fc195eSAndrew Gallatin } 2073b2fc195eSAndrew Gallatin 2074b2fc195eSAndrew Gallatin static int 20756d87a65dSAndrew Gallatin mxge_open(mxge_softc_t *sc) 2076b2fc195eSAndrew Gallatin { 20776d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2078b2fc195eSAndrew Gallatin int i, err; 2079b2fc195eSAndrew Gallatin bus_dmamap_t map; 2080b2fc195eSAndrew Gallatin 2081b2fc195eSAndrew Gallatin 20826d87a65dSAndrew Gallatin err = mxge_reset(sc); 2083b2fc195eSAndrew Gallatin if (err != 0) { 2084b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to reset\n"); 2085b2fc195eSAndrew Gallatin return EIO; 2086b2fc195eSAndrew Gallatin } 2087b2fc195eSAndrew Gallatin 2088b2fc195eSAndrew Gallatin if (MCLBYTES >= 20895e7d8541SAndrew Gallatin sc->ifp->if_mtu + ETHER_HDR_LEN + MXGEFW_PAD) 2090b2fc195eSAndrew Gallatin sc->big_bytes = MCLBYTES; 2091b2fc195eSAndrew Gallatin else 2092b2fc195eSAndrew Gallatin sc->big_bytes = MJUMPAGESIZE; 2093b2fc195eSAndrew Gallatin 20946d87a65dSAndrew Gallatin err = mxge_alloc_rings(sc); 2095b2fc195eSAndrew Gallatin if (err != 0) { 2096b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to allocate rings\n"); 2097b2fc195eSAndrew Gallatin return err; 2098b2fc195eSAndrew Gallatin } 2099b2fc195eSAndrew Gallatin 2100b2fc195eSAndrew Gallatin err = bus_setup_intr(sc->dev, sc->irq_res, 2101b2fc195eSAndrew Gallatin INTR_TYPE_NET | INTR_MPSAFE, 21026d87a65dSAndrew Gallatin mxge_intr, sc, &sc->ih); 2103b2fc195eSAndrew Gallatin if (err != 0) { 2104b2fc195eSAndrew Gallatin goto abort_with_rings; 2105b2fc195eSAndrew Gallatin } 2106b2fc195eSAndrew Gallatin 2107b2fc195eSAndrew Gallatin /* get the lanai pointers to the send and receive rings */ 2108b2fc195eSAndrew Gallatin 21095e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_GET_SEND_OFFSET, &cmd); 2110b2fc195eSAndrew Gallatin sc->tx.lanai = 2111b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); 21126d87a65dSAndrew Gallatin err |= mxge_send_cmd(sc, 21135e7d8541SAndrew Gallatin MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd); 2114b2fc195eSAndrew Gallatin sc->rx_small.lanai = 2115b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 21165e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd); 2117b2fc195eSAndrew Gallatin sc->rx_big.lanai = 2118b2fc195eSAndrew Gallatin (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); 2119b2fc195eSAndrew Gallatin 2120b2fc195eSAndrew Gallatin if (err != 0) { 2121b2fc195eSAndrew Gallatin device_printf(sc->dev, 2122b2fc195eSAndrew Gallatin "failed to get ring sizes or locations\n"); 2123b2fc195eSAndrew Gallatin err = EIO; 2124b2fc195eSAndrew Gallatin goto abort_with_irq; 2125b2fc195eSAndrew Gallatin } 2126b2fc195eSAndrew Gallatin 2127b2fc195eSAndrew Gallatin if (sc->wc) { 2128b2fc195eSAndrew Gallatin sc->tx.wc_fifo = sc->sram + 0x200000; 2129b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = sc->sram + 0x300000; 2130b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = sc->sram + 0x340000; 2131b2fc195eSAndrew Gallatin } else { 2132b2fc195eSAndrew Gallatin sc->tx.wc_fifo = 0; 2133b2fc195eSAndrew Gallatin sc->rx_small.wc_fifo = 0; 2134b2fc195eSAndrew Gallatin sc->rx_big.wc_fifo = 0; 2135b2fc195eSAndrew Gallatin } 2136b2fc195eSAndrew Gallatin 2137b2fc195eSAndrew Gallatin 2138b2fc195eSAndrew Gallatin /* stock receive rings */ 2139b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_small.mask; i++) { 2140b2fc195eSAndrew Gallatin map = sc->rx_small.info[i].map; 21416d87a65dSAndrew Gallatin err = mxge_get_buf_small(sc, map, i); 2142b2fc195eSAndrew Gallatin if (err) { 2143b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d smalls\n", 2144b2fc195eSAndrew Gallatin i, sc->rx_small.mask + 1); 2145b2fc195eSAndrew Gallatin goto abort; 2146b2fc195eSAndrew Gallatin } 2147b2fc195eSAndrew Gallatin } 2148b2fc195eSAndrew Gallatin for (i = 0; i <= sc->rx_big.mask; i++) { 2149b2fc195eSAndrew Gallatin map = sc->rx_big.info[i].map; 21506d87a65dSAndrew Gallatin err = mxge_get_buf_big(sc, map, i); 2151b2fc195eSAndrew Gallatin if (err) { 2152b2fc195eSAndrew Gallatin device_printf(sc->dev, "alloced %d/%d bigs\n", 2153b2fc195eSAndrew Gallatin i, sc->rx_big.mask + 1); 2154b2fc195eSAndrew Gallatin goto abort; 2155b2fc195eSAndrew Gallatin } 2156b2fc195eSAndrew Gallatin } 2157b2fc195eSAndrew Gallatin 2158b2fc195eSAndrew Gallatin /* Give the firmware the mtu and the big and small buffer 2159b2fc195eSAndrew Gallatin sizes. The firmware wants the big buf size to be a power 2160b2fc195eSAndrew Gallatin of two. Luckily, FreeBSD's clusters are powers of two */ 2161b2fc195eSAndrew Gallatin cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN; 21625e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_MTU, &cmd); 2163b2fc195eSAndrew Gallatin cmd.data0 = MHLEN; 21645e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, 2165b2fc195eSAndrew Gallatin &cmd); 2166b2fc195eSAndrew Gallatin cmd.data0 = sc->big_bytes; 21675e7d8541SAndrew Gallatin err |= mxge_send_cmd(sc, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd); 2168b2fc195eSAndrew Gallatin /* Now give him the pointer to the stats block */ 21696d87a65dSAndrew Gallatin cmd.data0 = MXGE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); 21706d87a65dSAndrew Gallatin cmd.data1 = MXGE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); 21715e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_SET_STATS_DMA, &cmd); 2172b2fc195eSAndrew Gallatin 2173b2fc195eSAndrew Gallatin if (err != 0) { 2174b2fc195eSAndrew Gallatin device_printf(sc->dev, "failed to setup params\n"); 2175b2fc195eSAndrew Gallatin goto abort; 2176b2fc195eSAndrew Gallatin } 2177b2fc195eSAndrew Gallatin 2178b2fc195eSAndrew Gallatin /* Finally, start the firmware running */ 21795e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_UP, &cmd); 2180b2fc195eSAndrew Gallatin if (err) { 2181b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring up link\n"); 2182b2fc195eSAndrew Gallatin goto abort; 2183b2fc195eSAndrew Gallatin } 2184b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 2185b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2186b2fc195eSAndrew Gallatin 2187b2fc195eSAndrew Gallatin return 0; 2188b2fc195eSAndrew Gallatin 2189b2fc195eSAndrew Gallatin 2190b2fc195eSAndrew Gallatin abort: 21916d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 2192b2fc195eSAndrew Gallatin abort_with_irq: 2193b2fc195eSAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 2194b2fc195eSAndrew Gallatin abort_with_rings: 21956d87a65dSAndrew Gallatin mxge_free_rings(sc); 2196b2fc195eSAndrew Gallatin return err; 2197b2fc195eSAndrew Gallatin } 2198b2fc195eSAndrew Gallatin 2199b2fc195eSAndrew Gallatin static int 22006d87a65dSAndrew Gallatin mxge_close(mxge_softc_t *sc) 2201b2fc195eSAndrew Gallatin { 22026d87a65dSAndrew Gallatin mxge_cmd_t cmd; 2203b2fc195eSAndrew Gallatin int err, old_down_cnt; 2204b2fc195eSAndrew Gallatin 2205b2fc195eSAndrew Gallatin sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2206b2fc195eSAndrew Gallatin old_down_cnt = sc->down_cnt; 2207b2fc195eSAndrew Gallatin mb(); 22085e7d8541SAndrew Gallatin err = mxge_send_cmd(sc, MXGEFW_CMD_ETHERNET_DOWN, &cmd); 2209b2fc195eSAndrew Gallatin if (err) { 2210b2fc195eSAndrew Gallatin device_printf(sc->dev, "Couldn't bring down link\n"); 2211b2fc195eSAndrew Gallatin } 2212b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2213b2fc195eSAndrew Gallatin /* wait for down irq */ 22146d87a65dSAndrew Gallatin (void)tsleep(&sc->down_cnt, PWAIT, "down mxge", hz); 2215b2fc195eSAndrew Gallatin } 2216b2fc195eSAndrew Gallatin if (old_down_cnt == sc->down_cnt) { 2217b2fc195eSAndrew Gallatin device_printf(sc->dev, "never got down irq\n"); 2218b2fc195eSAndrew Gallatin } 2219b2fc195eSAndrew Gallatin if (sc->ih != NULL) 2220b2fc195eSAndrew Gallatin bus_teardown_intr(sc->dev, sc->irq_res, sc->ih); 22216d87a65dSAndrew Gallatin mxge_free_mbufs(sc); 22226d87a65dSAndrew Gallatin mxge_free_rings(sc); 2223b2fc195eSAndrew Gallatin return 0; 2224b2fc195eSAndrew Gallatin } 2225b2fc195eSAndrew Gallatin 2226b2fc195eSAndrew Gallatin 2227b2fc195eSAndrew Gallatin static int 22286d87a65dSAndrew Gallatin mxge_media_change(struct ifnet *ifp) 2229b2fc195eSAndrew Gallatin { 2230b2fc195eSAndrew Gallatin return EINVAL; 2231b2fc195eSAndrew Gallatin } 2232b2fc195eSAndrew Gallatin 2233b2fc195eSAndrew Gallatin static int 22346d87a65dSAndrew Gallatin mxge_change_mtu(mxge_softc_t *sc, int mtu) 2235b2fc195eSAndrew Gallatin { 2236b2fc195eSAndrew Gallatin struct ifnet *ifp = sc->ifp; 2237b2fc195eSAndrew Gallatin int real_mtu, old_mtu; 2238b2fc195eSAndrew Gallatin int err = 0; 2239b2fc195eSAndrew Gallatin 2240b2fc195eSAndrew Gallatin 2241b2fc195eSAndrew Gallatin real_mtu = mtu + ETHER_HDR_LEN; 22426d87a65dSAndrew Gallatin if ((real_mtu > MXGE_MAX_ETHER_MTU) || 2243b2fc195eSAndrew Gallatin real_mtu < 60) 2244b2fc195eSAndrew Gallatin return EINVAL; 2245b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2246b2fc195eSAndrew Gallatin old_mtu = ifp->if_mtu; 2247b2fc195eSAndrew Gallatin ifp->if_mtu = mtu; 2248b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 22496d87a65dSAndrew Gallatin mxge_close(sc); 22506d87a65dSAndrew Gallatin err = mxge_open(sc); 2251b2fc195eSAndrew Gallatin if (err != 0) { 2252b2fc195eSAndrew Gallatin ifp->if_mtu = old_mtu; 22536d87a65dSAndrew Gallatin mxge_close(sc); 22546d87a65dSAndrew Gallatin (void) mxge_open(sc); 2255b2fc195eSAndrew Gallatin } 2256b2fc195eSAndrew Gallatin } 2257b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2258b2fc195eSAndrew Gallatin return err; 2259b2fc195eSAndrew Gallatin } 2260b2fc195eSAndrew Gallatin 2261b2fc195eSAndrew Gallatin static void 22626d87a65dSAndrew Gallatin mxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 2263b2fc195eSAndrew Gallatin { 22646d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2265b2fc195eSAndrew Gallatin 2266b2fc195eSAndrew Gallatin 2267b2fc195eSAndrew Gallatin if (sc == NULL) 2268b2fc195eSAndrew Gallatin return; 2269b2fc195eSAndrew Gallatin ifmr->ifm_status = IFM_AVALID; 2270b2fc195eSAndrew Gallatin ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; 2271b2fc195eSAndrew Gallatin ifmr->ifm_active = IFM_AUTO | IFM_ETHER; 2272b2fc195eSAndrew Gallatin ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; 2273b2fc195eSAndrew Gallatin } 2274b2fc195eSAndrew Gallatin 2275b2fc195eSAndrew Gallatin static int 22766d87a65dSAndrew Gallatin mxge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 2277b2fc195eSAndrew Gallatin { 22786d87a65dSAndrew Gallatin mxge_softc_t *sc = ifp->if_softc; 2279b2fc195eSAndrew Gallatin struct ifreq *ifr = (struct ifreq *)data; 2280b2fc195eSAndrew Gallatin int err, mask; 2281b2fc195eSAndrew Gallatin 2282b2fc195eSAndrew Gallatin err = 0; 2283b2fc195eSAndrew Gallatin switch (command) { 2284b2fc195eSAndrew Gallatin case SIOCSIFADDR: 2285b2fc195eSAndrew Gallatin case SIOCGIFADDR: 2286b2fc195eSAndrew Gallatin err = ether_ioctl(ifp, command, data); 2287b2fc195eSAndrew Gallatin break; 2288b2fc195eSAndrew Gallatin 2289b2fc195eSAndrew Gallatin case SIOCSIFMTU: 22906d87a65dSAndrew Gallatin err = mxge_change_mtu(sc, ifr->ifr_mtu); 2291b2fc195eSAndrew Gallatin break; 2292b2fc195eSAndrew Gallatin 2293b2fc195eSAndrew Gallatin case SIOCSIFFLAGS: 2294b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2295b2fc195eSAndrew Gallatin if (ifp->if_flags & IFF_UP) { 2296b2fc195eSAndrew Gallatin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 22976d87a65dSAndrew Gallatin err = mxge_open(sc); 2298b2fc195eSAndrew Gallatin } else { 2299b2fc195eSAndrew Gallatin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 23006d87a65dSAndrew Gallatin mxge_close(sc); 2301b2fc195eSAndrew Gallatin } 2302b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2303b2fc195eSAndrew Gallatin break; 2304b2fc195eSAndrew Gallatin 2305b2fc195eSAndrew Gallatin case SIOCADDMULTI: 2306b2fc195eSAndrew Gallatin case SIOCDELMULTI: 2307b2fc195eSAndrew Gallatin err = 0; 2308b2fc195eSAndrew Gallatin break; 2309b2fc195eSAndrew Gallatin 2310b2fc195eSAndrew Gallatin case SIOCSIFCAP: 2311b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2312b2fc195eSAndrew Gallatin mask = ifr->ifr_reqcap ^ ifp->if_capenable; 2313b2fc195eSAndrew Gallatin if (mask & IFCAP_TXCSUM) { 2314b2fc195eSAndrew Gallatin if (IFCAP_TXCSUM & ifp->if_capenable) { 2315b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_TXCSUM; 2316b2fc195eSAndrew Gallatin ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP); 2317b2fc195eSAndrew Gallatin } else { 2318b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_TXCSUM; 2319b2fc195eSAndrew Gallatin ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); 2320b2fc195eSAndrew Gallatin } 2321b2fc195eSAndrew Gallatin } else if (mask & IFCAP_RXCSUM) { 2322b2fc195eSAndrew Gallatin if (IFCAP_RXCSUM & ifp->if_capenable) { 2323b2fc195eSAndrew Gallatin ifp->if_capenable &= ~IFCAP_RXCSUM; 23245e7d8541SAndrew Gallatin sc->csum_flag = 0; 2325b2fc195eSAndrew Gallatin } else { 2326b2fc195eSAndrew Gallatin ifp->if_capenable |= IFCAP_RXCSUM; 23275e7d8541SAndrew Gallatin sc->csum_flag = 1; 2328b2fc195eSAndrew Gallatin } 2329b2fc195eSAndrew Gallatin } 2330b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2331b2fc195eSAndrew Gallatin break; 2332b2fc195eSAndrew Gallatin 2333b2fc195eSAndrew Gallatin case SIOCGIFMEDIA: 2334b2fc195eSAndrew Gallatin err = ifmedia_ioctl(ifp, (struct ifreq *)data, 2335b2fc195eSAndrew Gallatin &sc->media, command); 2336b2fc195eSAndrew Gallatin break; 2337b2fc195eSAndrew Gallatin 2338b2fc195eSAndrew Gallatin default: 2339b2fc195eSAndrew Gallatin err = ENOTTY; 2340b2fc195eSAndrew Gallatin } 2341b2fc195eSAndrew Gallatin return err; 2342b2fc195eSAndrew Gallatin } 2343b2fc195eSAndrew Gallatin 2344b2fc195eSAndrew Gallatin static void 23456d87a65dSAndrew Gallatin mxge_fetch_tunables(mxge_softc_t *sc) 2346b2fc195eSAndrew Gallatin { 2347b2fc195eSAndrew Gallatin 23486d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.flow_control_enabled", 23496d87a65dSAndrew Gallatin &mxge_flow_control); 23506d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.intr_coal_delay", 23516d87a65dSAndrew Gallatin &mxge_intr_coal_delay); 23526d87a65dSAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.nvidia_ecrc_enable", 23536d87a65dSAndrew Gallatin &mxge_nvidia_ecrc_enable); 23545e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.deassert_wait", 23555e7d8541SAndrew Gallatin &mxge_deassert_wait); 23565e7d8541SAndrew Gallatin TUNABLE_INT_FETCH("hw.mxge.verbose", 23575e7d8541SAndrew Gallatin &mxge_verbose); 2358b2fc195eSAndrew Gallatin 23595e7d8541SAndrew Gallatin if (bootverbose) 23605e7d8541SAndrew Gallatin mxge_verbose = 1; 23616d87a65dSAndrew Gallatin if (mxge_intr_coal_delay < 0 || mxge_intr_coal_delay > 10*1000) 23626d87a65dSAndrew Gallatin mxge_intr_coal_delay = 30; 23636d87a65dSAndrew Gallatin sc->pause = mxge_flow_control; 2364b2fc195eSAndrew Gallatin } 2365b2fc195eSAndrew Gallatin 2366b2fc195eSAndrew Gallatin static int 23676d87a65dSAndrew Gallatin mxge_attach(device_t dev) 2368b2fc195eSAndrew Gallatin { 23696d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2370b2fc195eSAndrew Gallatin struct ifnet *ifp; 2371b2fc195eSAndrew Gallatin size_t bytes; 23725e7d8541SAndrew Gallatin int rid, err; 2373b2fc195eSAndrew Gallatin uint16_t cmd; 2374b2fc195eSAndrew Gallatin 2375b2fc195eSAndrew Gallatin sc->dev = dev; 23766d87a65dSAndrew Gallatin mxge_fetch_tunables(sc); 2377b2fc195eSAndrew Gallatin 2378b2fc195eSAndrew Gallatin err = bus_dma_tag_create(NULL, /* parent */ 2379b2fc195eSAndrew Gallatin 1, /* alignment */ 2380b2fc195eSAndrew Gallatin 4096, /* boundary */ 2381b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* low */ 2382b2fc195eSAndrew Gallatin BUS_SPACE_MAXADDR, /* high */ 2383b2fc195eSAndrew Gallatin NULL, NULL, /* filter */ 23846d87a65dSAndrew Gallatin MXGE_MAX_ETHER_MTU, /* maxsize */ 23855e7d8541SAndrew Gallatin MXGE_MAX_SEND_DESC, /* num segs */ 2386b2fc195eSAndrew Gallatin 4096, /* maxsegsize */ 2387b2fc195eSAndrew Gallatin 0, /* flags */ 2388b2fc195eSAndrew Gallatin NULL, NULL, /* lock */ 2389b2fc195eSAndrew Gallatin &sc->parent_dmat); /* tag */ 2390b2fc195eSAndrew Gallatin 2391b2fc195eSAndrew Gallatin if (err != 0) { 2392b2fc195eSAndrew Gallatin device_printf(sc->dev, "Err %d allocating parent dmat\n", 2393b2fc195eSAndrew Gallatin err); 2394b2fc195eSAndrew Gallatin goto abort_with_nothing; 2395b2fc195eSAndrew Gallatin } 2396b2fc195eSAndrew Gallatin 2397b2fc195eSAndrew Gallatin ifp = sc->ifp = if_alloc(IFT_ETHER); 2398b2fc195eSAndrew Gallatin if (ifp == NULL) { 2399b2fc195eSAndrew Gallatin device_printf(dev, "can not if_alloc()\n"); 2400b2fc195eSAndrew Gallatin err = ENOSPC; 2401b2fc195eSAndrew Gallatin goto abort_with_parent_dmat; 2402b2fc195eSAndrew Gallatin } 2403b2fc195eSAndrew Gallatin mtx_init(&sc->cmd_lock, NULL, 2404b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2405b2fc195eSAndrew Gallatin mtx_init(&sc->tx_lock, device_get_nameunit(dev), 2406b2fc195eSAndrew Gallatin MTX_NETWORK_LOCK, MTX_DEF); 2407b2fc195eSAndrew Gallatin sx_init(&sc->driver_lock, device_get_nameunit(dev)); 2408b2fc195eSAndrew Gallatin 2409b2fc195eSAndrew Gallatin /* Enable DMA and Memory space access */ 2410b2fc195eSAndrew Gallatin pci_enable_busmaster(dev); 2411b2fc195eSAndrew Gallatin cmd = pci_read_config(dev, PCIR_COMMAND, 2); 2412b2fc195eSAndrew Gallatin cmd |= PCIM_CMD_MEMEN; 2413b2fc195eSAndrew Gallatin pci_write_config(dev, PCIR_COMMAND, cmd, 2); 2414b2fc195eSAndrew Gallatin 2415b2fc195eSAndrew Gallatin /* Map the board into the kernel */ 2416b2fc195eSAndrew Gallatin rid = PCIR_BARS; 2417b2fc195eSAndrew Gallatin sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, 2418b2fc195eSAndrew Gallatin ~0, 1, RF_ACTIVE); 2419b2fc195eSAndrew Gallatin if (sc->mem_res == NULL) { 2420b2fc195eSAndrew Gallatin device_printf(dev, "could not map memory\n"); 2421b2fc195eSAndrew Gallatin err = ENXIO; 2422b2fc195eSAndrew Gallatin goto abort_with_lock; 2423b2fc195eSAndrew Gallatin } 2424b2fc195eSAndrew Gallatin sc->sram = rman_get_virtual(sc->mem_res); 2425b2fc195eSAndrew Gallatin sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; 2426b2fc195eSAndrew Gallatin if (sc->sram_size > rman_get_size(sc->mem_res)) { 2427b2fc195eSAndrew Gallatin device_printf(dev, "impossible memory region size %ld\n", 2428b2fc195eSAndrew Gallatin rman_get_size(sc->mem_res)); 2429b2fc195eSAndrew Gallatin err = ENXIO; 2430b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2431b2fc195eSAndrew Gallatin } 2432b2fc195eSAndrew Gallatin 2433b2fc195eSAndrew Gallatin /* make NULL terminated copy of the EEPROM strings section of 2434b2fc195eSAndrew Gallatin lanai SRAM */ 24356d87a65dSAndrew Gallatin bzero(sc->eeprom_strings, MXGE_EEPROM_STRINGS_SIZE); 2436b2fc195eSAndrew Gallatin bus_space_read_region_1(rman_get_bustag(sc->mem_res), 2437b2fc195eSAndrew Gallatin rman_get_bushandle(sc->mem_res), 24386d87a65dSAndrew Gallatin sc->sram_size - MXGE_EEPROM_STRINGS_SIZE, 2439b2fc195eSAndrew Gallatin sc->eeprom_strings, 24406d87a65dSAndrew Gallatin MXGE_EEPROM_STRINGS_SIZE - 2); 24416d87a65dSAndrew Gallatin err = mxge_parse_strings(sc); 2442b2fc195eSAndrew Gallatin if (err != 0) 2443b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2444b2fc195eSAndrew Gallatin 2445b2fc195eSAndrew Gallatin /* Enable write combining for efficient use of PCIe bus */ 24466d87a65dSAndrew Gallatin mxge_enable_wc(sc); 2447b2fc195eSAndrew Gallatin 2448b2fc195eSAndrew Gallatin /* Allocate the out of band dma memory */ 24496d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->cmd_dma, 24506d87a65dSAndrew Gallatin sizeof (mxge_cmd_t), 64); 2451b2fc195eSAndrew Gallatin if (err != 0) 2452b2fc195eSAndrew Gallatin goto abort_with_mem_res; 2453b2fc195eSAndrew Gallatin sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; 24546d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->zeropad_dma, 64, 64); 2455b2fc195eSAndrew Gallatin if (err != 0) 2456b2fc195eSAndrew Gallatin goto abort_with_cmd_dma; 2457b2fc195eSAndrew Gallatin 24586d87a65dSAndrew Gallatin err = mxge_dma_alloc(sc, &sc->fw_stats_dma, 2459b2fc195eSAndrew Gallatin sizeof (*sc->fw_stats), 64); 2460b2fc195eSAndrew Gallatin if (err != 0) 2461b2fc195eSAndrew Gallatin goto abort_with_zeropad_dma; 24625e7d8541SAndrew Gallatin sc->fw_stats = (mcp_irq_data_t *)sc->fw_stats_dma.addr; 2463b2fc195eSAndrew Gallatin 2464b2fc195eSAndrew Gallatin 2465b2fc195eSAndrew Gallatin /* allocate interrupt queues */ 24665e7d8541SAndrew Gallatin bytes = mxge_max_intr_slots * sizeof (*sc->rx_done.entry); 24675e7d8541SAndrew Gallatin err = mxge_dma_alloc(sc, &sc->rx_done.dma, bytes, 4096); 2468b2fc195eSAndrew Gallatin if (err != 0) 24695e7d8541SAndrew Gallatin goto abort_with_fw_stats; 24705e7d8541SAndrew Gallatin sc->rx_done.entry = sc->rx_done.dma.addr; 24715e7d8541SAndrew Gallatin bzero(sc->rx_done.entry, bytes); 2472b2fc195eSAndrew Gallatin /* Add our ithread */ 2473b2fc195eSAndrew Gallatin rid = 0; 2474b2fc195eSAndrew Gallatin sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 2475b2fc195eSAndrew Gallatin 1, RF_SHAREABLE | RF_ACTIVE); 2476b2fc195eSAndrew Gallatin if (sc->irq_res == NULL) { 2477b2fc195eSAndrew Gallatin device_printf(dev, "could not alloc interrupt\n"); 24785e7d8541SAndrew Gallatin goto abort_with_rx_done; 2479b2fc195eSAndrew Gallatin } 2480b2fc195eSAndrew Gallatin 2481b2fc195eSAndrew Gallatin /* load the firmware */ 24826d87a65dSAndrew Gallatin mxge_select_firmware(sc); 2483b2fc195eSAndrew Gallatin 24846d87a65dSAndrew Gallatin err = mxge_load_firmware(sc); 2485b2fc195eSAndrew Gallatin if (err != 0) 2486b2fc195eSAndrew Gallatin goto abort_with_irq_res; 24875e7d8541SAndrew Gallatin sc->intr_coal_delay = mxge_intr_coal_delay; 24886d87a65dSAndrew Gallatin err = mxge_reset(sc); 2489b2fc195eSAndrew Gallatin if (err != 0) 2490b2fc195eSAndrew Gallatin goto abort_with_irq_res; 2491b2fc195eSAndrew Gallatin 2492b2fc195eSAndrew Gallatin /* hook into the network stack */ 2493b2fc195eSAndrew Gallatin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 2494b2fc195eSAndrew Gallatin ifp->if_baudrate = 100000000; 2495b2fc195eSAndrew Gallatin ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM; 2496b2fc195eSAndrew Gallatin ifp->if_hwassist = CSUM_TCP | CSUM_UDP; 2497b2fc195eSAndrew Gallatin ifp->if_capenable = ifp->if_capabilities; 24985e7d8541SAndrew Gallatin sc->csum_flag = 1; 24996d87a65dSAndrew Gallatin ifp->if_init = mxge_init; 2500b2fc195eSAndrew Gallatin ifp->if_softc = sc; 2501b2fc195eSAndrew Gallatin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 25026d87a65dSAndrew Gallatin ifp->if_ioctl = mxge_ioctl; 25036d87a65dSAndrew Gallatin ifp->if_start = mxge_start; 25046d87a65dSAndrew Gallatin ifp->if_watchdog = mxge_watchdog; 2505b2fc195eSAndrew Gallatin ether_ifattach(ifp, sc->mac_addr); 2506b2fc195eSAndrew Gallatin /* ether_ifattach sets mtu to 1500 */ 25076d87a65dSAndrew Gallatin ifp->if_mtu = MXGE_MAX_ETHER_MTU - ETHER_HDR_LEN; 2508b2fc195eSAndrew Gallatin 2509b2fc195eSAndrew Gallatin /* Initialise the ifmedia structure */ 25106d87a65dSAndrew Gallatin ifmedia_init(&sc->media, 0, mxge_media_change, 25116d87a65dSAndrew Gallatin mxge_media_status); 2512b2fc195eSAndrew Gallatin ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); 25136d87a65dSAndrew Gallatin mxge_add_sysctls(sc); 2514b2fc195eSAndrew Gallatin return 0; 2515b2fc195eSAndrew Gallatin 2516b2fc195eSAndrew Gallatin abort_with_irq_res: 2517b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 25185e7d8541SAndrew Gallatin abort_with_rx_done: 25195e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 25205e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 25215e7d8541SAndrew Gallatin abort_with_fw_stats: 25226d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 2523b2fc195eSAndrew Gallatin abort_with_zeropad_dma: 25246d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 2525b2fc195eSAndrew Gallatin abort_with_cmd_dma: 25266d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 2527b2fc195eSAndrew Gallatin abort_with_mem_res: 2528b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 2529b2fc195eSAndrew Gallatin abort_with_lock: 2530b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 2531b2fc195eSAndrew Gallatin mtx_destroy(&sc->cmd_lock); 2532b2fc195eSAndrew Gallatin mtx_destroy(&sc->tx_lock); 2533b2fc195eSAndrew Gallatin sx_destroy(&sc->driver_lock); 2534b2fc195eSAndrew Gallatin if_free(ifp); 2535b2fc195eSAndrew Gallatin abort_with_parent_dmat: 2536b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 2537b2fc195eSAndrew Gallatin 2538b2fc195eSAndrew Gallatin abort_with_nothing: 2539b2fc195eSAndrew Gallatin return err; 2540b2fc195eSAndrew Gallatin } 2541b2fc195eSAndrew Gallatin 2542b2fc195eSAndrew Gallatin static int 25436d87a65dSAndrew Gallatin mxge_detach(device_t dev) 2544b2fc195eSAndrew Gallatin { 25456d87a65dSAndrew Gallatin mxge_softc_t *sc = device_get_softc(dev); 2546b2fc195eSAndrew Gallatin 2547b2fc195eSAndrew Gallatin sx_xlock(&sc->driver_lock); 2548b2fc195eSAndrew Gallatin if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) 25496d87a65dSAndrew Gallatin mxge_close(sc); 2550b2fc195eSAndrew Gallatin sx_xunlock(&sc->driver_lock); 2551b2fc195eSAndrew Gallatin ether_ifdetach(sc->ifp); 2552091feecdSAndrew Gallatin mxge_dummy_rdma(sc, 0); 2553b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 25545e7d8541SAndrew Gallatin sc->rx_done.entry = NULL; 25555e7d8541SAndrew Gallatin mxge_dma_free(&sc->rx_done.dma); 25566d87a65dSAndrew Gallatin mxge_dma_free(&sc->fw_stats_dma); 25576d87a65dSAndrew Gallatin mxge_dma_free(&sc->zeropad_dma); 25586d87a65dSAndrew Gallatin mxge_dma_free(&sc->cmd_dma); 2559b2fc195eSAndrew Gallatin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); 2560b2fc195eSAndrew Gallatin pci_disable_busmaster(dev); 2561b2fc195eSAndrew Gallatin mtx_destroy(&sc->cmd_lock); 2562b2fc195eSAndrew Gallatin mtx_destroy(&sc->tx_lock); 2563b2fc195eSAndrew Gallatin sx_destroy(&sc->driver_lock); 2564b2fc195eSAndrew Gallatin if_free(sc->ifp); 2565b2fc195eSAndrew Gallatin bus_dma_tag_destroy(sc->parent_dmat); 2566b2fc195eSAndrew Gallatin return 0; 2567b2fc195eSAndrew Gallatin } 2568b2fc195eSAndrew Gallatin 2569b2fc195eSAndrew Gallatin static int 25706d87a65dSAndrew Gallatin mxge_shutdown(device_t dev) 2571b2fc195eSAndrew Gallatin { 2572b2fc195eSAndrew Gallatin return 0; 2573b2fc195eSAndrew Gallatin } 2574b2fc195eSAndrew Gallatin 2575b2fc195eSAndrew Gallatin /* 2576b2fc195eSAndrew Gallatin This file uses Myri10GE driver indentation. 2577b2fc195eSAndrew Gallatin 2578b2fc195eSAndrew Gallatin Local Variables: 2579b2fc195eSAndrew Gallatin c-file-style:"linux" 2580b2fc195eSAndrew Gallatin tab-width:8 2581b2fc195eSAndrew Gallatin End: 2582b2fc195eSAndrew Gallatin */ 2583