Index: share/man/man4/myri10ge.4 =================================================================== RCS file: share/man/man4/myri10ge.4 diff -N share/man/man4/myri10ge.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man4/myri10ge.4 9 Feb 2006 16:52:10 -0000 @@ -0,0 +1,142 @@ +.\" Copyright (c) 2006, Myricom Inc +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright notice, +.\" this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" 3. Neither the name of the Myricom Inc nor the names of its +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" * Other names and brands may be claimed as the property of others. +.\" +.\" $FreeBSD$ +.\" +.Dd February 9, 2006 +.Dt MYRI10GE 4 +.Os +.Sh NAME +.Nm myri10ge +.Nd "Myricom Myri10GE 10 Gigabit Ethernet adapter driver" +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device myri10ge" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_myri10ge_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for PCI Express 10 Gigabit Ethernet adapters based on +the Myricom LANai Z8E chip. +The driver supports Transmit/Receive checksum offload +and Jumbo Frames. +For further hardware information, see http://www.myri.com +.Pp +For questions related to hardware requirements, +refer to the documentation supplied with your Myri10GE adapter. +All hardware requirements listed apply to use with +.Fx . +.Pp +Support for Jumbo Frames is provided via the interface MTU setting. +Selecting an MTU larger than 1500 bytes with the +.Xr ifconfig 8 +utility configures the adapter to receive and transmit Jumbo Frames. +The maximum MTU size for Jumbo Frames is 9000. +.Pp +For more information on configuring this device, see +.Xr ifconfig 8 . +.Sh HARDWARE +The +.Nm +driver supports 10 Gigabit Ethernet adapters based on the +Myricom LANai Z8E chips: +.Pp +.Bl -bullet -compact +.It +Myricom 10GBase-CX4 (10G-PCIE-8A-C, 10G-PCIE-8AL-C) +.It +Myricom 10GBase-R (10G-PCIE-8A-R, 10G-PCIE-8AL-R) +.It +Myricom 10G XAUI over ribbon fiber (10G-PCIE-8A-Q, 10G-PCIE-8AL-Q) +.El +.Sh LOADER TUNABLES +Tunables can be set at the +.Xr loader 8 +prompt before booting the kernel or stored in +.Xr loader.conf 5 . +.Bl -tag -width indent +.It Va hw.myri10ge.flow_control_enabled +Whether or not hardware flow control is enabled on the adapter. +The default value is 1. +.It Va hw.myri10ge.intr_coal_delay +This value delays the generation of all interrupts in units of +1 microsecond. The default value is 30. +.It Va hw.myri10ge.skip_pio_read +This value determines whether or not the driver may omit doing a +pio read in the interrupt handler which ensures that the interrupt +line has been deasserted when using xPIC interrupts. The default +value is 0. +.El +.Sh DIAGNOSTICS +.Bl -diag +.It "myri10ge%d: Unable to allocate bus resource: memory" +A fatal initialization error has occurred. +.It "myri10ge%d: Unable to allocate bus resource: interrupt" +A fatal initialization error has occurred. +.It "myri10ge%d: Could not find firmware image %s" +The appropriate firmware kld module was not installed. This is a +fatal initialization error. +.El +.Sh SUPPORT +For general information and support, +go to the Myricom support website at: +.Pa http://www.myri.com/scs . +.Pp +If an issue is identified with the released source code on the supported kernel +with a supported adapter, email the specific information related to the +issue to +.Aq help@myri.com . +.Sh SEE ALSO +.Xr arp 4 , +.Xr netintro 4 , +.Xr ng_ether 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Andrew Gallatin Aq gallatin@freebsd.org . + Index: sys/dev/myri10ge/eth_z8e.dat =================================================================== RCS file: sys/dev/myri10ge/eth_z8e.dat diff -N sys/dev/myri10ge/eth_z8e.dat Binary files /dev/null and eth_z8e.dat differ Index: sys/dev/myri10ge/ethp_z8e.dat =================================================================== RCS file: sys/dev/myri10ge/ethp_z8e.dat diff -N sys/dev/myri10ge/ethp_z8e.dat Binary files /dev/null and ethp_z8e.dat differ Index: sys/dev/myri10ge/if_myri10ge.c =================================================================== RCS file: sys/dev/myri10ge/if_myri10ge.c diff -N sys/dev/myri10ge/if_myri10ge.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/myri10ge/if_myri10ge.c 9 Feb 2006 15:59:26 -0000 @@ -0,0 +1,2424 @@ +/******************************************************************************* + +Copyright (c) 2006, Myricom Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Myricom Inc, nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include /* for DELAY */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* tunable params */ +static int myri10ge_nvidia_ecrc_enable = 1; +static int myri10ge_max_intr_slots = 128; +static int myri10ge_intr_coal_delay = 30; +static int myri10ge_skip_pio_read = 0; +static int myri10ge_flow_control = 1; +static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e"; +static char *myri10ge_fw_aligned = "myri10ge_eth_z8e"; + +static int myri10ge_probe(device_t dev); +static int myri10ge_attach(device_t dev); +static int myri10ge_detach(device_t dev); +static int myri10ge_shutdown(device_t dev); +static void myri10ge_intr(void *arg); + +static device_method_t myri10ge_methods[] = +{ + /* Device interface */ + DEVMETHOD(device_probe, myri10ge_probe), + DEVMETHOD(device_attach, myri10ge_attach), + DEVMETHOD(device_detach, myri10ge_detach), + DEVMETHOD(device_shutdown, myri10ge_shutdown), + {0, 0} +}; + +static driver_t myri10ge_driver = +{ + "myri10ge", + myri10ge_methods, + sizeof(myri10ge_softc_t), +}; + +static devclass_t myri10ge_devclass; + +/* Declare ourselves to be a child of the PCI bus.*/ +DRIVER_MODULE(myri10ge, pci, myri10ge_driver, myri10ge_devclass, 0, 0); +MODULE_DEPEND(myri10ge, firmware, 1, 1, 1); + +static int +myri10ge_probe(device_t dev) +{ + if ((pci_get_vendor(dev) == MYRI10GE_PCI_VENDOR_MYRICOM) && + (pci_get_device(dev) == MYRI10GE_PCI_DEVICE_Z8E)) { + device_set_desc(dev, "Myri10G-PCIE-8A"); + return 0; + } + return ENXIO; +} + +static void +myri10ge_enable_wc(myri10ge_softc_t *sc) +{ + struct mem_range_desc mrdesc; + vm_paddr_t pa; + vm_offset_t len; + int err, action; + + pa = rman_get_start(sc->mem_res); + len = rman_get_size(sc->mem_res); + mrdesc.mr_base = pa; + mrdesc.mr_len = len; + mrdesc.mr_flags = MDF_WRITECOMBINE; + action = MEMRANGE_SET_UPDATE; + strcpy((char *)&mrdesc.mr_owner, "myri10ge"); + err = mem_range_attr_set(&mrdesc, &action); + if (err != 0) { + device_printf(sc->dev, + "w/c failed for pa 0x%lx, len 0x%lx, err = %d\n", + (unsigned long)pa, (unsigned long)len, err); + } else { + sc->wc = 1; + } +} + + +/* callback to get our DMA address */ +static void +myri10ge_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, + int error) +{ + if (error == 0) { + *(bus_addr_t *) arg = segs->ds_addr; + } +} + +static int +myri10ge_dma_alloc(device_t dev, myri10ge_dma_t *dma, size_t bytes, + bus_size_t alignment) +{ + int err; + + /* allocate DMAable memory tags */ + err = bus_dma_tag_create(NULL, /* parent */ + alignment, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + bytes, /* maxsize */ + 1, /* num segs */ + 4096, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &dma->dmat); /* tag */ + if (err != 0) { + device_printf(dev, "couldn't alloc tag (err = %d)\n", err); + return err; + } + + /* allocate DMAable memory & map */ + err = bus_dmamem_alloc(dma->dmat, &dma->addr, + (BUS_DMA_WAITOK | BUS_DMA_COHERENT + | BUS_DMA_ZERO), &dma->map); + if (err != 0) { + device_printf(dev, "couldn't alloc mem (err = %d)\n", err); + goto abort_with_dmat; + } + + /* load the memory */ + err = bus_dmamap_load(dma->dmat, dma->map, dma->addr, bytes, + myri10ge_dmamap_callback, + (void *)&dma->bus_addr, 0); + if (err != 0) { + device_printf(dev, "couldn't load map (err = %d)\n", err); + goto abort_with_mem; + } + return 0; + +abort_with_mem: + bus_dmamem_free(dma->dmat, dma->addr, dma->map); +abort_with_dmat: + (void)bus_dma_tag_destroy(dma->dmat); + return err; +} + + +static void +myri10ge_dma_free(myri10ge_dma_t *dma) +{ + bus_dmamap_unload(dma->dmat, dma->map); + bus_dmamem_free(dma->dmat, dma->addr, dma->map); + (void)bus_dma_tag_destroy(dma->dmat); +} + +/* + * The eeprom strings on the lanaiX have the format + * SN=x\0 + * MAC=x:x:x:x:x:x\0 + * PC=text\0 + */ + +static int +myri10ge_parse_strings(myri10ge_softc_t *sc) +{ +#define MYRI10GE_NEXT_STRING(p) while(ptr < limit && *ptr++) + + char *ptr, *limit; + int i, found_mac; + + ptr = sc->eeprom_strings; + limit = sc->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE; + found_mac = 0; + while (ptr < limit && *ptr != '\0') { + if (memcmp(ptr, "MAC=", 4) == 0) { + ptr+=4; + sc->mac_addr_string = ptr; + for (i = 0; i < 6; i++) { + if ((ptr + 2) > limit) + goto abort; + sc->mac_addr[i] = strtoul(ptr, NULL, 16); + found_mac = 1; + ptr += 3; + } + } else if (memcmp(ptr, "PC=", 4) == 0) { + sc->product_code_string = ptr; + } + MYRI10GE_NEXT_STRING(ptr); + } + + if (found_mac) + return 0; + + abort: + device_printf(sc->dev, "failed to parse eeprom_strings\n"); + + return ENXIO; +} + +#if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ +void *pmap_mapdev(vm_paddr_t, vm_size_t); +void pmap_unmapdev(vm_offset_t, vm_size_t); + +static int +myri10ge_enable_nvidia_ecrc(myri10ge_softc_t *sc, device_t pdev) +{ + uint32_t val; + unsigned long off; + char *va, *cfgptr; + uint16_t vendor_id, device_id; + uintptr_t bus, slot, func, ivend, idev; + uint32_t *ptr32; + + /* See if we can, by some miracle, access the extended + config space */ + val = pci_read_config(pdev, 0x178, 4); + if (val != 0xffffffff) { + val |= 0x40; + pci_write_config(pdev, 0x178, val, 4); + return 0; + } + /* Rather than using normal pci config space writes, we must + * map the Nvidia config space ourselves. This is because on + * opteron/nvidia class machine the 0xe000000 mapping is + * handled by the nvidia chipset, that means the internal PCI + * device (the on-chip northbridge), or the amd-8131 bridge + * and things behind them are not visible by this method. + */ + + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_BUS, &bus); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_SLOT, &slot); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_FUNCTION, &func); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_VENDOR, &ivend); + BUS_READ_IVAR(device_get_parent(pdev), pdev, + PCI_IVAR_DEVICE, &idev); + + off = 0xe0000000UL + + 0x00100000UL * (unsigned long)bus + + 0x00001000UL * (unsigned long)(func + + 8 * slot); + + /* map it into the kernel */ + va = pmap_mapdev(trunc_page((vm_paddr_t)off), PAGE_SIZE); + + + if (va == NULL) { + device_printf(sc->dev, "pmap_kenter_temporary didn't\n"); + return EIO; + } + /* get a pointer to the config space mapped into the kernel */ + cfgptr = va + (off & PAGE_MASK); + + /* make sure that we can really access it */ + vendor_id = *(uint16_t *)(cfgptr + PCIR_VENDOR); + device_id = *(uint16_t *)(cfgptr + PCIR_DEVICE); + if (! (vendor_id == ivend && device_id == idev)) { + device_printf(sc->dev, "mapping failed: 0x%x:0x%x\n", + vendor_id, device_id); + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + return EIO; + } + + ptr32 = (uint32_t*)(cfgptr + 0x178); + val = *ptr32; + + if (val == 0xffffffff) { + device_printf(sc->dev, "extended mapping failed\n"); + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + return EIO; + } + *ptr32 = val | 0x40; + pmap_unmapdev((vm_offset_t)va, PAGE_SIZE); + device_printf(sc->dev, + "Enabled ECRC on upstream Nvidia bridge at %d:%d:%d\n", + (int)bus, (int)slot, (int)func); + return 0; +} +#else +static int +myri10ge_enable_nvidia_ecrc(myri10ge_softc_t *sc, device_t pdev) +{ + device_printf(sc->dev, + "Nforce 4 chipset on non-x86/amd64!?!?!\n"); + return ENXIO; +} +#endif +/* + * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput + * when the PCI-E Completion packets are aligned on an 8-byte + * boundary. Some PCI-E chip sets always align Completion packets; on + * the ones that do not, the alignment can be enforced by enabling + * ECRC generation (if supported). + * + * When PCI-E Completion packets are not aligned, it is actually more + * efficient to limit Read-DMA transactions to 2KB, rather than 4KB. + * + * If the driver can neither enable ECRC nor verify that it has + * already been enabled, then it must use a firmware image which works + * around unaligned completion packets (ethp_z8e.dat), and it should + * also ensure that it never gives the device a Read-DMA which is + * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is + * enabled, then the driver should use the aligned (eth_z8e.dat) + * firmware image, and set tx.boundary to 4KB. + */ + +static void +myri10ge_select_firmware(myri10ge_softc_t *sc) +{ + int err, aligned = 0; + device_t pdev; + uint16_t pvend, pdid; + + pdev = device_get_parent(device_get_parent(sc->dev)); + if (pdev == NULL) { + device_printf(sc->dev, "could not find parent?\n"); + goto abort; + } + pvend = pci_read_config(pdev, PCIR_VENDOR, 2); + pdid = pci_read_config(pdev, PCIR_DEVICE, 2); + + /* see if we can enable ECRC's on an upstream + Nvidia bridge */ + if (myri10ge_nvidia_ecrc_enable && + (pvend == 0x10de && pdid == 0x005d)) { + err = myri10ge_enable_nvidia_ecrc(sc, pdev); + if (err == 0) { + aligned = 1; + device_printf(sc->dev, + "Assuming aligned completions (ECRC)\n"); + } + } + /* see if the upstream bridge is known to + provided aligned completions */ + if (/* HT2000 */ (pvend == 0x1166 && pdid == 0x0132) || + /* Ontario */ (pvend == 0x10b5 && pdid == 0x8532)) { + device_printf(sc->dev, + "Assuming aligned completions (0x%x:0x%x)\n", + pvend, pdid); + } + +abort: + if (aligned) { + sc->fw_name = myri10ge_fw_aligned; + sc->tx.boundary = 4096; + } else { + sc->fw_name = myri10ge_fw_unaligned; + sc->tx.boundary = 2048; + } +} + +union qualhack +{ + const char *ro_char; + char *rw_char; +}; + + +static int +myri10ge_load_firmware_helper(myri10ge_softc_t *sc, uint32_t *limit) +{ + struct firmware *fw; + const mcp_gen_header_t *hdr; + unsigned hdr_offset; + const char *fw_data; + union qualhack hack; + int status; + + + fw = firmware_get(sc->fw_name); + + if (fw == NULL) { + device_printf(sc->dev, "Could not find firmware image %s\n", + sc->fw_name); + return ENOENT; + } + if (fw->datasize > *limit || + fw->datasize < MCP_HEADER_PTR_OFFSET + 4) { + device_printf(sc->dev, "Firmware image %s too large (%d/%d)\n", + sc->fw_name, (int)fw->datasize, (int) *limit); + status = ENOSPC; + goto abort_with_fw; + } + *limit = fw->datasize; + + /* check id */ + fw_data = (const char *)fw->data; + hdr_offset = htobe32(*(const uint32_t *) + (fw_data + MCP_HEADER_PTR_OFFSET)); + if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->datasize) { + device_printf(sc->dev, "Bad firmware file"); + status = EIO; + goto abort_with_fw; + } + hdr = (const void*)(fw_data + hdr_offset); + if (be32toh(hdr->mcp_type) != MCP_TYPE_ETH) { + device_printf(sc->dev, "Bad firmware type: 0x%x\n", + be32toh(hdr->mcp_type)); + status = EIO; + goto abort_with_fw; + } + + /* save firmware version for sysctl */ + strncpy(sc->fw_version, hdr->version, sizeof (sc->fw_version)); + printf("firmware id: %s\n", hdr->version); + + hack.ro_char = fw_data; + /* Copy the inflated firmware to NIC SRAM. */ + myri10ge_pio_copy(&sc->sram[MYRI10GE_FW_OFFSET], + hack.rw_char, *limit); + + status = 0; +abort_with_fw: + firmware_put(fw, FIRMWARE_UNLOAD); + return status; +} + +/* + * Enable or disable periodic RDMAs from the host to make certain + * chipsets resend dropped PCIe messages + */ + +static void +myri10ge_dummy_rdma(myri10ge_softc_t *sc, int enable) +{ + char buf_bytes[72]; + volatile uint32_t *confirm; + volatile char *submit; + uint32_t *buf, dma_low, dma_high; + int i; + + buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + /* clear confirmation addr */ + confirm = (volatile uint32_t *)sc->cmd; + *confirm = 0; + mb(); + + /* send an rdma command to the PCIe engine, and wait for the + response in the confirmation address. The firmware should + write a -1 there to indicate it is alive and well + */ + + dma_low = MYRI10GE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MYRI10GE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + buf[0] = htobe32(dma_high); /* confirm addr MSW */ + buf[1] = htobe32(dma_low); /* confirm addr LSW */ + buf[2] = htobe32(0xffffffff); /* confirm data */ + dma_low = MYRI10GE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr); + dma_high = MYRI10GE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr); + buf[3] = htobe32(dma_high); /* dummy addr MSW */ + buf[4] = htobe32(dma_low); /* dummy addr LSW */ + buf[5] = htobe32(enable); /* enable? */ + + + submit = (volatile char *)(sc->sram + 0xfc01c0); + + myri10ge_pio_copy(submit, buf, 64); + mb(); + DELAY(1000); + mb(); + i = 0; + while (*confirm != 0xffffffff && i < 20) { + DELAY(1000); + i++; + } + if (*confirm != 0xffffffff) { + device_printf(sc->dev, "dummy rdma %s failed (%p = 0x%x)", + (enable ? "enable" : "disable"), confirm, + *confirm); + } + return; +} + +static int +myri10ge_send_cmd(myri10ge_softc_t *sc, uint32_t cmd, + myri10ge_cmd_t *data) +{ + mcp_cmd_t *buf; + char buf_bytes[sizeof(*buf) + 8]; + volatile mcp_cmd_response_t *response = sc->cmd; + volatile char *cmd_addr = sc->sram + MYRI10GE_MCP_CMD_OFFSET; + uint32_t dma_low, dma_high; + int sleep_total = 0; + + /* ensure buf is aligned to 8 bytes */ + buf = (mcp_cmd_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + buf->data0 = htobe32(data->data0); + buf->data1 = htobe32(data->data1); + buf->data2 = htobe32(data->data2); + buf->cmd = htobe32(cmd); + dma_low = MYRI10GE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MYRI10GE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + + buf->response_addr.low = htobe32(dma_low); + buf->response_addr.high = htobe32(dma_high); + mtx_lock(&sc->cmd_lock); + response->result = 0xffffffff; + mb(); + myri10ge_pio_copy((volatile void *)cmd_addr, buf, sizeof (*buf)); + + /* wait up to 2 seconds */ + for (sleep_total = 0; sleep_total < (2 * 1000); sleep_total += 10) { + bus_dmamap_sync(sc->cmd_dma.dmat, + sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); + mb(); + if (response->result != 0xffffffff) { + if (response->result == 0) { + data->data0 = be32toh(response->data); + mtx_unlock(&sc->cmd_lock); + return 0; + } else { + device_printf(sc->dev, + "myri10ge: command %d " + "failed, result = %d\n", + cmd, be32toh(response->result)); + mtx_unlock(&sc->cmd_lock); + return ENXIO; + } + } + DELAY(1000 * 10); + } + mtx_unlock(&sc->cmd_lock); + device_printf(sc->dev, "myri10ge: command %d timed out" + "result = %d\n", + cmd, be32toh(response->result)); + return EAGAIN; +} + + +static int +myri10ge_load_firmware(myri10ge_softc_t *sc) +{ + volatile uint32_t *confirm; + volatile char *submit; + char buf_bytes[72]; + uint32_t *buf, size, dma_low, dma_high; + int status, i; + + buf = (uint32_t *)((unsigned long)(buf_bytes + 7) & ~7UL); + + size = sc->sram_size; + status = myri10ge_load_firmware_helper(sc, &size); + if (status) { + device_printf(sc->dev, "firmware loading failed\n"); + return status; + } + /* clear confirmation addr */ + confirm = (volatile uint32_t *)sc->cmd; + *confirm = 0; + mb(); + /* send a reload command to the bootstrap MCP, and wait for the + response in the confirmation address. The firmware should + write a -1 there to indicate it is alive and well + */ + + dma_low = MYRI10GE_LOWPART_TO_U32(sc->cmd_dma.bus_addr); + dma_high = MYRI10GE_HIGHPART_TO_U32(sc->cmd_dma.bus_addr); + + buf[0] = htobe32(dma_high); /* confirm addr MSW */ + buf[1] = htobe32(dma_low); /* confirm addr LSW */ + buf[2] = htobe32(0xffffffff); /* confirm data */ + + /* FIX: All newest firmware should un-protect the bottom of + the sram before handoff. However, the very first interfaces + do not. Therefore the handoff copy must skip the first 8 bytes + */ + /* where the code starts*/ + buf[3] = htobe32(MYRI10GE_FW_OFFSET + 8); + buf[4] = htobe32(size - 8); /* length of code */ + buf[5] = htobe32(8); /* where to copy to */ + buf[6] = htobe32(0); /* where to jump to */ + + submit = (volatile char *)(sc->sram + 0xfc0000); + myri10ge_pio_copy(submit, buf, 64); + mb(); + DELAY(1000); + mb(); + i = 0; + while (*confirm != 0xffffffff && i < 20) { + DELAY(1000*10); + i++; + bus_dmamap_sync(sc->cmd_dma.dmat, + sc->cmd_dma.map, BUS_DMASYNC_POSTREAD); + } + if (*confirm != 0xffffffff) { + device_printf(sc->dev,"handoff failed (%p = 0x%x)", + confirm, *confirm); + + return ENXIO; + } + myri10ge_dummy_rdma(sc, 1); + return 0; +} + +static int +myri10ge_update_mac_address(myri10ge_softc_t *sc) +{ + myri10ge_cmd_t cmd; + uint8_t *addr = sc->mac_addr; + int status; + + + cmd.data0 = ((addr[0] << 24) | (addr[1] << 16) + | (addr[2] << 8) | addr[3]); + + cmd.data1 = ((addr[4] << 8) | (addr[5])); + + status = myri10ge_send_cmd(sc, MYRI10GE_MCP_SET_MAC_ADDRESS, &cmd); + return status; +} + +static int +myri10ge_change_pause(myri10ge_softc_t *sc, int pause) +{ + myri10ge_cmd_t cmd; + int status; + + if (pause) + status = myri10ge_send_cmd(sc, + MYRI10GE_MCP_ENABLE_FLOW_CONTROL, + &cmd); + else + status = myri10ge_send_cmd(sc, + MYRI10GE_MCP_DISABLE_FLOW_CONTROL, + &cmd); + + if (status) { + device_printf(sc->dev, "Failed to set flow control mode\n"); + return ENXIO; + } + sc->pause = pause; + return 0; +} + +static void +myri10ge_change_promisc(myri10ge_softc_t *sc, int promisc) +{ + myri10ge_cmd_t cmd; + int status; + + if (promisc) + status = myri10ge_send_cmd(sc, + MYRI10GE_MCP_ENABLE_PROMISC, + &cmd); + else + status = myri10ge_send_cmd(sc, + MYRI10GE_MCP_DISABLE_PROMISC, + &cmd); + + if (status) { + device_printf(sc->dev, "Failed to set promisc mode\n"); + } +} + +static int +myri10ge_reset(myri10ge_softc_t *sc) +{ + + myri10ge_cmd_t cmd; + int status, i; + + /* try to send a reset command to the card to see if it + is alive */ + memset(&cmd, 0, sizeof (cmd)); + status = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_RESET, &cmd); + if (status != 0) { + device_printf(sc->dev, "failed reset\n"); + return ENXIO; + } + + /* Now exchange information about interrupts */ + + cmd.data0 = (uint32_t) + (myri10ge_max_intr_slots * sizeof (*sc->intr.q[0])); + status = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_SET_INTRQ_SIZE, &cmd); + for (i = 0; (status == 0) && (i < MYRI10GE_NUM_INTRQS); i++) { + cmd.data0 = MYRI10GE_LOWPART_TO_U32(sc->intr.dma[i].bus_addr); + cmd.data1 = MYRI10GE_HIGHPART_TO_U32(sc->intr.dma[i].bus_addr); + status |= + myri10ge_send_cmd(sc, (i + + MYRI10GE_MCP_CMD_SET_INTRQ0_DMA), + &cmd); + } + + cmd.data0 = sc->intr_coal_delay = myri10ge_intr_coal_delay; + status |= myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_SET_INTR_COAL_DELAY, &cmd); + + if (sc->msi_enabled) { + status |= myri10ge_send_cmd + (sc, MYRI10GE_MCP_CMD_GET_IRQ_ACK_OFFSET, &cmd); + } else { + status |= myri10ge_send_cmd + (sc, MYRI10GE_MCP_CMD_GET_IRQ_ACK_DEASSERT_OFFSET, + &cmd); + } + if (status != 0) { + device_printf(sc->dev, "failed set interrupt parameters\n"); + return status; + } + sc->irq_claim = (volatile uint32_t *)(sc->sram + cmd.data0); + + /* reset mcp/driver shared state back to 0 */ + sc->intr.seqnum = 0; + sc->intr.intrq = 0; + sc->intr.slot = 0; + sc->tx.req = 0; + sc->tx.done = 0; + sc->rx_big.cnt = 0; + sc->rx_small.cnt = 0; + status = myri10ge_update_mac_address(sc); + myri10ge_change_promisc(sc, 0); + myri10ge_change_pause(sc, sc->pause); + return status; +} + +static int +myri10ge_change_intr_coal(SYSCTL_HANDLER_ARGS) +{ + myri10ge_cmd_t cmd; + myri10ge_softc_t *sc; + unsigned int intr_coal_delay; + int err; + + sc = arg1; + intr_coal_delay = sc->intr_coal_delay; + err = sysctl_handle_int(oidp, &intr_coal_delay, arg2, req); + if (err != 0) { + return err; + } + if (intr_coal_delay == sc->intr_coal_delay) + return 0; + + if (intr_coal_delay == 0 || intr_coal_delay > 1000*1000) + return EINVAL; + + sx_xlock(&sc->driver_lock); + cmd.data0 = intr_coal_delay; + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_SET_INTR_COAL_DELAY, + &cmd); + if (err == 0) { + sc->intr_coal_delay = intr_coal_delay; + } + sx_xunlock(&sc->driver_lock); + return err; +} + +static int +myri10ge_change_flow_control(SYSCTL_HANDLER_ARGS) +{ + myri10ge_softc_t *sc; + unsigned int enabled; + int err; + + sc = arg1; + enabled = sc->pause; + err = sysctl_handle_int(oidp, &enabled, arg2, req); + if (err != 0) { + return err; + } + if (enabled == sc->pause) + return 0; + + sx_xlock(&sc->driver_lock); + err = myri10ge_change_pause(sc, enabled); + sx_xunlock(&sc->driver_lock); + return err; +} + +static int +myri10ge_handle_be32(SYSCTL_HANDLER_ARGS) +{ + int err; + + if (arg1 == NULL) + return EFAULT; + arg2 = be32toh(*(int *)arg1); + arg1 = NULL; + err = sysctl_handle_int(oidp, arg1, arg2, req); + + return err; +} + +static void +myri10ge_add_sysctls(myri10ge_softc_t *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *children; + mcp_stats_t *fw; + + ctx = device_get_sysctl_ctx(sc->dev); + children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); + fw = sc->fw_stats; + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "intr_coal_delay", + CTLTYPE_INT|CTLFLAG_RW, sc, + 0, myri10ge_change_intr_coal, + "I", "interrupt coalescing delay in usecs"); + + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "flow_control_enabled", + CTLTYPE_INT|CTLFLAG_RW, sc, + 0, myri10ge_change_flow_control, + "I", "interrupt coalescing delay in usecs"); + + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "skip_pio_read", + CTLFLAG_RW, &myri10ge_skip_pio_read, + 0, "Skip pio read in interrupt handler"); + + /* stats block from firmware is in network byte order. + Need to swap it */ + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "link_up", + CTLTYPE_INT|CTLFLAG_RD, &fw->link_up, + 0, myri10ge_handle_be32, + "I", "link up"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_link_overflow", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_link_overflow, + 0, myri10ge_handle_be32, + "I", "dropped_link_overflow"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_link_error_or_filtered", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_link_error_or_filtered, + 0, myri10ge_handle_be32, + "I", "dropped_link_error_or_filtered"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_runt", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_runt, + 0, myri10ge_handle_be32, + "I", "dropped_runt"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_overrun", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_overrun, + 0, myri10ge_handle_be32, + "I", "dropped_overrun"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_no_small_buffer", + CTLTYPE_INT|CTLFLAG_RD, + &fw->dropped_no_small_buffer, + 0, myri10ge_handle_be32, + "I", "dropped_no_small_buffer"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_no_big_buffer", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_no_big_buffer, + 0, myri10ge_handle_be32, + "I", "dropped_no_big_buffer"); + SYSCTL_ADD_PROC(ctx, children, OID_AUTO, + "dropped_interrupt_busy", + CTLTYPE_INT|CTLFLAG_RD, &fw->dropped_interrupt_busy, + 0, myri10ge_handle_be32, + "I", "dropped_interrupt_busy"); + + /* host counters exported for debugging */ + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_req", + CTLFLAG_RD, &sc->tx.req, + 0, "tx_req"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "tx_done", + CTLFLAG_RD, &sc->tx.done, + 0, "tx_done"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "rx_small_cnt", + CTLFLAG_RD, &sc->rx_small.cnt, + 0, "rx_small_cnt"); + SYSCTL_ADD_INT(ctx, children, OID_AUTO, + "rx_big_cnt", + CTLFLAG_RD, &sc->rx_big.cnt, + 0, "rx_small_cnt"); + +} + +/* copy an array of mcp_kreq_ether_send_t's to the mcp. Copy + backwards one at a time and handle ring wraps */ + +static inline void +myri10ge_submit_req_backwards(myri10ge_tx_buf_t *tx, + mcp_kreq_ether_send_t *src, int cnt) +{ + int idx, starting_slot; + starting_slot = tx->req; + while (cnt > 1) { + cnt--; + idx = (starting_slot + cnt) & tx->mask; + myri10ge_pio_copy(&tx->lanai[idx], + &src[cnt], sizeof(*src)); + mb(); + } +} + +/* + * copy an array of mcp_kreq_ether_send_t's to the mcp. Copy + * at most 32 bytes at a time, so as to avoid involving the software + * pio handler in the nic. We re-write the first segment's flags + * to mark them valid only after writing the entire chain + */ + +static inline void +myri10ge_submit_req(myri10ge_tx_buf_t *tx, mcp_kreq_ether_send_t *src, + int cnt) +{ + int idx, i; + uint32_t *src_ints; + volatile uint32_t *dst_ints; + mcp_kreq_ether_send_t *srcp; + volatile mcp_kreq_ether_send_t *dstp, *dst; + + + idx = tx->req & tx->mask; + + src->flags &= ~(htobe16(MYRI10GE_MCP_ETHER_FLAGS_VALID)); + mb(); + dst = dstp = &tx->lanai[idx]; + srcp = src; + + if ((idx + cnt) < tx->mask) { + for (i = 0; i < (cnt - 1); i += 2) { + myri10ge_pio_copy(dstp, srcp, 2 * sizeof(*src)); + mb(); /* force write every 32 bytes */ + srcp += 2; + dstp += 2; + } + } else { + /* submit all but the first request, and ensure + that it is submitted below */ + myri10ge_submit_req_backwards(tx, src, cnt); + i = 0; + } + if (i < cnt) { + /* submit the first request */ + myri10ge_pio_copy(dstp, srcp, sizeof(*src)); + mb(); /* barrier before setting valid flag */ + } + + /* re-write the last 32-bits with the valid flags */ + src->flags |= htobe16(MYRI10GE_MCP_ETHER_FLAGS_VALID); + src_ints = (uint32_t *)src; + src_ints+=3; + dst_ints = (volatile uint32_t *)dst; + dst_ints+=3; + *dst_ints = *src_ints; + tx->req += cnt; + mb(); +} + +static inline void +myri10ge_submit_req_wc(myri10ge_tx_buf_t *tx, + mcp_kreq_ether_send_t *src, int cnt) +{ + tx->req += cnt; + mb(); + while (cnt >= 4) { + myri10ge_pio_copy((volatile char *)tx->wc_fifo, src, 64); + mb(); + src += 4; + cnt -= 4; + } + if (cnt > 0) { + /* pad it to 64 bytes. The src is 64 bytes bigger than it + needs to be so that we don't overrun it */ + myri10ge_pio_copy(tx->wc_fifo + (cnt<<18), src, 64); + mb(); + } +} + +static void +myri10ge_encap(myri10ge_softc_t *sc, struct mbuf *m) +{ + mcp_kreq_ether_send_t *req, *req_list; + char req_bytes[8 + sizeof (*req_list) * (MYRI10GE_MCP_ETHER_MAX_SEND_DESC + 4)]; + bus_dma_segment_t seg_list[MYRI10GE_MCP_ETHER_MAX_SEND_DESC]; + bus_dma_segment_t *seg; + struct mbuf *m_tmp; + struct ifnet *ifp; + myri10ge_tx_buf_t *tx; + struct ether_header *eh; + struct ip *ip; + int cnt, cum_len, err, i, idx; + uint16_t flags, pseudo_hdr_offset; + uint8_t cksum_offset; + + + + ifp = sc->ifp; + tx = &sc->tx; + + /* (try to) map the frame for DMA */ + idx = tx->req & tx->mask; + err = bus_dmamap_load_mbuf_sg(tx->dmat, tx->info[idx].map, + m, seg_list, &cnt, + BUS_DMA_NOWAIT); + if (err == EFBIG) { + /* Too many segments in the chain. Try + to defrag */ + m_tmp = m_defrag(m, M_NOWAIT); + if (m_tmp == NULL) { + goto drop; + } + m = m_tmp; + err = bus_dmamap_load_mbuf_sg(tx->dmat, + tx->info[idx].map, + m, seg_list, &cnt, + BUS_DMA_NOWAIT); + } + if (err != 0) { + device_printf(sc->dev, "bus_dmamap_load_mbuf_sg returned %d\n", + err); + goto drop; + } + bus_dmamap_sync(tx->dmat, tx->info[idx].map, + BUS_DMASYNC_PREWRITE); + + /* ensure req_list entries are aligned to 8 bytes */ + req = req_list = (mcp_kreq_ether_send_t *) + ((unsigned long)(req_bytes + 7) & ~7UL); + cksum_offset = 0; + flags = htobe16(MYRI10GE_MCP_ETHER_FLAGS_VALID | + MYRI10GE_MCP_ETHER_FLAGS_NOT_LAST); + + /* checksum offloading? */ + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA)) { + eh = mtod(m, struct ether_header *); + ip = (struct ip *) (eh + 1); + cksum_offset = sizeof(*eh) + (ip->ip_hl << 2); + pseudo_hdr_offset = cksum_offset + m->m_pkthdr.csum_data; + req->pseudo_hdr_offset = htobe16(pseudo_hdr_offset); + req->cksum_offset = cksum_offset; + flags |= htobe16(MYRI10GE_MCP_ETHER_FLAGS_CKSUM); + } + if (m->m_pkthdr.len < 512) + req->flags = htobe16(MYRI10GE_MCP_ETHER_FLAGS_FIRST | + MYRI10GE_MCP_ETHER_FLAGS_SMALL); + else + req->flags = htobe16(MYRI10GE_MCP_ETHER_FLAGS_FIRST); + + /* convert segments into a request list */ + cum_len = 0; + seg = seg_list; + for (i = 0; i < cnt; i++) { + req->addr_low = + htobe32(MYRI10GE_LOWPART_TO_U32(seg->ds_addr)); + req->addr_high = + htobe32(MYRI10GE_HIGHPART_TO_U32(seg->ds_addr)); + req->length = htobe16(seg->ds_len); + req->cksum_offset = cksum_offset; + if (cksum_offset > seg->ds_len) + cksum_offset -= seg->ds_len; + else + cksum_offset = 0; + req->flags |= flags | ((cum_len & 1) * + htobe16(MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD)); + cum_len += seg->ds_len; + seg++; + req++; + req->flags = 0; + } + req--; + /* pad runts to 60 bytes */ + if (cum_len < 60) { + req++; + req->addr_low = + htobe32(MYRI10GE_LOWPART_TO_U32(sc->zeropad_dma.bus_addr)); + req->addr_high = + htobe32(MYRI10GE_HIGHPART_TO_U32(sc->zeropad_dma.bus_addr)); + req->length = htobe16(60 - cum_len); + req->cksum_offset = cksum_offset; + req->flags |= flags | ((cum_len & 1) * + htobe16(MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD)); + cnt++; + } + req->flags &= ~(htobe16(MYRI10GE_MCP_ETHER_FLAGS_NOT_LAST)); + tx->info[idx].m = m; + if (tx->wc_fifo == NULL) + myri10ge_submit_req(tx, req_list, cnt); + else + myri10ge_submit_req_wc(tx, req_list, cnt); + return; + +drop: + m_freem(m); + ifp->if_oerrors++; + return; +} + + +static void +myri10ge_start_locked(myri10ge_softc_t *sc) +{ + int avail; + struct mbuf *m; + struct ifnet *ifp; + + + ifp = sc->ifp; + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { + /* dequeue the packet */ + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + /* let BPF see it */ + BPF_MTAP(ifp, m); + + /* give it to the nic */ + myri10ge_encap(sc, m); + + /* leave an extra slot keep the ring from wrapping */ + avail = sc->tx.mask - (sc->tx.req - sc->tx.done); + if (avail < MYRI10GE_MCP_ETHER_MAX_SEND_DESC) { + sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; + return; + } + } +} + +static void +myri10ge_start(struct ifnet *ifp) +{ + myri10ge_softc_t *sc = ifp->if_softc; + + + mtx_lock(&sc->tx_lock); + myri10ge_start_locked(sc); + mtx_unlock(&sc->tx_lock); +} + +static int +myri10ge_get_buf_small(myri10ge_softc_t *sc, bus_dmamap_t map, int idx) +{ + bus_dma_segment_t seg; + struct mbuf *m; + myri10ge_rx_buf_t *rx = &sc->rx_small; + int cnt, err; + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) { + rx->alloc_fail++; + err = ENOBUFS; + goto done; + } + m->m_len = MHLEN; + err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, + &seg, &cnt, BUS_DMA_NOWAIT); + if (err != 0) { + m_free(m); + goto done; + } + rx->info[idx].m = m; + rx->shadow[idx].addr_low = + htobe32(MYRI10GE_LOWPART_TO_U32(seg.ds_addr)); + rx->shadow[idx].addr_high = + htobe32(MYRI10GE_HIGHPART_TO_U32(seg.ds_addr)); + +done: + if ((idx & 7) == 7) { + myri10ge_pio_copy(&rx->lanai[idx - 7], + &rx->shadow[idx - 7], + 8 * sizeof (*rx->lanai)); + mb(); + } + return err; +} + +static int +myri10ge_get_buf_big(myri10ge_softc_t *sc, bus_dmamap_t map, int idx) +{ + bus_dma_segment_t seg; + struct mbuf *m; + myri10ge_rx_buf_t *rx = &sc->rx_big; + int cnt, err; + + m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->big_bytes); + if (m == NULL) { + rx->alloc_fail++; + err = ENOBUFS; + goto done; + } + m->m_len = sc->big_bytes; + err = bus_dmamap_load_mbuf_sg(rx->dmat, map, m, + &seg, &cnt, BUS_DMA_NOWAIT); + if (err != 0) { + m_free(m); + goto done; + } + rx->info[idx].m = m; + rx->shadow[idx].addr_low = + htobe32(MYRI10GE_LOWPART_TO_U32(seg.ds_addr)); + rx->shadow[idx].addr_high = + htobe32(MYRI10GE_HIGHPART_TO_U32(seg.ds_addr)); + +done: + if ((idx & 7) == 7) { + myri10ge_pio_copy(&rx->lanai[idx - 7], + &rx->shadow[idx - 7], + 8 * sizeof (*rx->lanai)); + mb(); + } + return err; +} + +static inline void +myri10ge_rx_done_big(myri10ge_softc_t *sc, int len, int csum, int flags) +{ + struct ifnet *ifp; + struct mbuf *m = 0; /* -Wunitialized */ + struct mbuf *m_prev = 0; /* -Wunitialized */ + struct mbuf *m_head = 0; + bus_dmamap_t old_map; + myri10ge_rx_buf_t *rx; + int idx; + + + rx = &sc->rx_big; + ifp = sc->ifp; + while (len > 0) { + idx = rx->cnt & rx->mask; + rx->cnt++; + /* save a pointer to the received mbuf */ + m = rx->info[idx].m; + /* try to replace the received mbuf */ + if (myri10ge_get_buf_big(sc, rx->extra_map, idx)) { + goto drop; + } + /* unmap the received buffer */ + old_map = rx->info[idx].map; + bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rx->dmat, old_map); + + /* swap the bus_dmamap_t's */ + rx->info[idx].map = rx->extra_map; + rx->extra_map = old_map; + + /* chain multiple segments together */ + if (!m_head) { + m_head = m; + /* mcp implicitly skips 1st bytes so that + * packet is properly aligned */ + m->m_data += MYRI10GE_MCP_ETHER_PAD; + m->m_pkthdr.len = len; + m->m_len = sc->big_bytes - MYRI10GE_MCP_ETHER_PAD; + } else { + m->m_len = sc->big_bytes; + m->m_flags &= ~M_PKTHDR; + m_prev->m_next = m; + } + len -= m->m_len; + m_prev = m; + } + + /* trim trailing garbage from the last mbuf in the chain. If + * there is any garbage, len will be negative */ + m->m_len += len; + + /* if the checksum is valid, mark it in the mbuf header */ + if (sc->csum_flag & flags) { + m_head->m_pkthdr.csum_data = csum; + m_head->m_pkthdr.csum_flags = CSUM_DATA_VALID; + } + + /* pass the frame up the stack */ + m_head->m_pkthdr.rcvif = ifp; + ifp->if_ipackets++; + (*ifp->if_input)(ifp, m_head); + return; + +drop: + /* drop the frame -- the old mbuf(s) are re-cycled by running + every slot through the allocator */ + if (m_head) { + len -= sc->big_bytes; + m_freem(m_head); + } else { + len -= (sc->big_bytes + MYRI10GE_MCP_ETHER_PAD); + } + while ((int)len > 0) { + idx = rx->cnt & rx->mask; + rx->cnt++; + m = rx->info[idx].m; + if (0 == (myri10ge_get_buf_big(sc, rx->extra_map, idx))) { + m_freem(m); + /* unmap the received buffer */ + old_map = rx->info[idx].map; + bus_dmamap_sync(rx->dmat, old_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rx->dmat, old_map); + + /* swap the bus_dmamap_t's */ + rx->info[idx].map = rx->extra_map; + rx->extra_map = old_map; + } + len -= sc->big_bytes; + } + + ifp->if_ierrors++; + +} + + +static inline void +myri10ge_rx_done_small(myri10ge_softc_t *sc, uint32_t len, + uint32_t csum, uint32_t flags) +{ + struct ifnet *ifp; + struct mbuf *m; + myri10ge_rx_buf_t *rx; + bus_dmamap_t old_map; + int idx; + + ifp = sc->ifp; + rx = &sc->rx_small; + idx = rx->cnt & rx->mask; + rx->cnt++; + /* save a pointer to the received mbuf */ + m = rx->info[idx].m; + /* try to replace the received mbuf */ + if (myri10ge_get_buf_small(sc, rx->extra_map, idx)) { + /* drop the frame -- the old mbuf is re-cycled */ + ifp->if_ierrors++; + return; + } + + /* unmap the received buffer */ + old_map = rx->info[idx].map; + bus_dmamap_sync(rx->dmat, old_map, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rx->dmat, old_map); + + /* swap the bus_dmamap_t's */ + rx->info[idx].map = rx->extra_map; + rx->extra_map = old_map; + + /* mcp implicitly skips 1st 2 bytes so that packet is properly + * aligned */ + m->m_data += MYRI10GE_MCP_ETHER_PAD; + + /* if the checksum is valid, mark it in the mbuf header */ + if (sc->csum_flag & flags) { + m->m_pkthdr.csum_data = csum; + m->m_pkthdr.csum_flags = CSUM_DATA_VALID; + } + + /* pass the frame up the stack */ + m->m_pkthdr.rcvif = ifp; + m->m_len = m->m_pkthdr.len = len; + ifp->if_ipackets++; + (*ifp->if_input)(ifp, m); +} + +static inline void +myri10ge_tx_done(myri10ge_softc_t *sc, uint32_t mcp_idx) +{ + struct ifnet *ifp; + myri10ge_tx_buf_t *tx; + struct mbuf *m; + bus_dmamap_t map; + int idx; + + tx = &sc->tx; + ifp = sc->ifp; + while (tx->done != mcp_idx) { + idx = tx->done & tx->mask; + tx->done++; + m = tx->info[idx].m; + /* mbuf and DMA map only attached to the first + segment per-mbuf */ + if (m != NULL) { + ifp->if_opackets++; + tx->info[idx].m = NULL; + map = tx->info[idx].map; + bus_dmamap_unload(tx->dmat, map); + m_freem(m); + } + } + + /* If we have space, clear IFF_OACTIVE to tell the stack that + its OK to send packets */ + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE && + tx->req - tx->done < (tx->mask + 1)/4) { + mtx_lock(&sc->tx_lock); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + myri10ge_start_locked(sc); + mtx_unlock(&sc->tx_lock); + } +} + +static void +myri10ge_dump_interrupt_queues(myri10ge_softc_t *sc, int maxslot) +{ + int intrq, slot, type; + static int call_cnt = 0; + + /* only do it a few times to avoid filling the message buffer */ + if (call_cnt > 10) + return; + + call_cnt++; + + device_printf(sc->dev, "--------- Dumping interrupt queue state ----- \n"); + device_printf(sc->dev, "currently expecting interrupts on queue %d\n", + sc->intr.intrq); + device_printf(sc->dev, " q slot status \n"); + device_printf(sc->dev, "--- ---- -------- \n"); + for (intrq = 0; intrq < 2; intrq++) { + for (slot = 0; slot <= maxslot; slot++) { + type = sc->intr.q[intrq][slot].type; +#if 0 + if (type == 0 && slot != 0) + continue; +#endif + device_printf(sc->dev, "[%d]:[%d]: type = 0x%x\n", intrq, slot, + type); + device_printf(sc->dev, "[%d]:[%d]: flag = 0x%x\n", intrq, slot, + sc->intr.q[intrq][slot].flag); + device_printf(sc->dev, "[%d]:[%d]: index = 0x%x\n", intrq, slot, + be16toh(sc->intr.q[intrq][slot].index)); + device_printf(sc->dev, "[%d]:[%d]: seqnum = 0x%x\n", intrq, slot, + (unsigned int)be32toh(sc->intr.q[intrq][slot].seqnum)); + device_printf(sc->dev, "[%d]:[%d]: data0 = 0x%x\n", intrq, slot, + (unsigned int)be32toh(sc->intr.q[intrq][slot].data0)); + device_printf(sc->dev, "[%d]:[%d]: data1 = 0x%x\n", intrq, slot, + (unsigned int)be32toh(sc->intr.q[intrq][slot].data1)); + + } + } + +} + +static inline void +myri10ge_claim_irq(myri10ge_softc_t *sc) +{ + volatile uint32_t dontcare; + + + *sc->irq_claim = 0; + mb(); + + /* do a PIO read to ensure that PIO write to claim the irq has + hit the nic before we exit the interrupt handler */ + if (!myri10ge_skip_pio_read) { + dontcare = *(volatile uint32_t *)sc->sram; + mb(); + } +} + +static void +myri10ge_intr(void *arg) +{ + myri10ge_softc_t *sc = arg; + int intrq, claimed, flags, count, length, ip_csum; + uint32_t raw, slot; + uint8_t type; + + + intrq = sc->intr.intrq; + claimed = 0; + bus_dmamap_sync(sc->intr.dma[intrq].dmat, + sc->intr.dma[intrq].map, BUS_DMASYNC_POSTREAD); + if (sc->msi_enabled) { + /* We know we can immediately claim the interrupt */ + myri10ge_claim_irq(sc); + claimed = 1; + } else { + /* Check to see if we have the last event in the queue + ready. If so, ack it as early as possible. This + allows more time to get the interrupt line + de-asserted prior to the EOI and reduces the chance + of seeing a spurious irq caused by the interrupt + line remaining high after EOI */ + + slot = be16toh(sc->intr.q[intrq][0].index) - 1; + if (slot < myri10ge_max_intr_slots && + sc->intr.q[intrq][slot].type != 0 && + sc->intr.q[intrq][slot].flag != 0) { + myri10ge_claim_irq(sc); + claimed = 1; + } + } + + /* walk each slot in the current queue, processing events until + we reach an event with a zero type */ + for (slot = sc->intr.slot; slot < myri10ge_max_intr_slots; slot++) { + type = sc->intr.q[intrq][slot].type; + + /* check for partially completed DMA of events when + using non-MSI interrupts */ + if (__predict_false(!claimed)) { + mb(); + /* look if there is somscing in the queue */ + if (type == 0) { + /* save the current slot for the next + * time we (re-)enter this routine */ + if (sc->intr.slot == slot) { + sc->intr.spurious++; + } + sc->intr.slot = slot; + return; + } + } + if (__predict_false(htobe32(sc->intr.q[intrq][slot].seqnum) != + sc->intr.seqnum++)) { + device_printf(sc->dev, "Bad interrupt!\n"); + device_printf(sc->dev, + "bad irq seqno" + "(got 0x%x, expected 0x%x) \n", + (unsigned int)htobe32(sc->intr.q[intrq][slot].seqnum), + sc->intr.seqnum); + device_printf(sc->dev, "intrq = %d, slot = %d\n", + intrq, slot); + myri10ge_dump_interrupt_queues(sc, slot); + device_printf(sc->dev, + "Disabling futher interrupt handling\n"); + bus_teardown_intr(sc->dev, sc->irq_res, + sc->ih); + sc->ih = NULL; + return; + } + + switch (type) { + case MYRI10GE_MCP_INTR_ETHER_SEND_DONE: + myri10ge_tx_done(sc, be32toh(sc->intr.q[intrq][slot].data0)); + + if (__predict_true(sc->intr.q[intrq][slot].data1 == 0)) + break; + + /* check the link state. Don't bother to + * byteswap, since it can just be 0 or 1 */ + if (sc->link_state != sc->fw_stats->link_up) { + sc->link_state = sc->fw_stats->link_up; + if (sc->link_state) { + device_printf(sc->dev, + "link up\n"); + } else { + device_printf(sc->dev, + "link down\n"); + } + } + break; + + + case MYRI10GE_MCP_INTR_ETHER_RECV_SMALL: + raw = be32toh(sc->intr.q[intrq][slot].data0); + count = 0xff & raw; + flags = raw >> 8; + raw = be32toh(sc->intr.q[intrq][slot].data1); + ip_csum = raw >> 16; + length = 0xffff & raw; + myri10ge_rx_done_small(sc, length, ip_csum, + flags); + break; + + case MYRI10GE_MCP_INTR_ETHER_RECV_BIG: + raw = be32toh(sc->intr.q[intrq][slot].data0); + count = 0xff & raw; + flags = raw >> 8; + raw = be32toh(sc->intr.q[intrq][slot].data1); + ip_csum = raw >> 16; + length = 0xffff & raw; + myri10ge_rx_done_big(sc, length, ip_csum, + flags); + + break; + + case MYRI10GE_MCP_INTR_LINK_CHANGE: + /* not yet implemented in firmware */ + break; + + case MYRI10GE_MCP_INTR_ETHER_DOWN: + sc->down_cnt++; + wakeup(&sc->down_cnt); + break; + + default: + device_printf(sc->dev, "Unknown interrupt type %d\n", + type); + } + sc->intr.q[intrq][slot].type = 0; + if (sc->intr.q[intrq][slot].flag != 0) { + if (!claimed) { + myri10ge_claim_irq(sc); + } + sc->intr.slot = 0; + sc->intr.q[intrq][slot].flag = 0; + sc->intr.intrq = ((intrq + 1) & 1); + return; + } + } + + /* we should never be here unless we're on a shared irq and we have + not finished setting up the device */ + return; +} + +static void +myri10ge_watchdog(struct ifnet *ifp) +{ + printf("%s called\n", __FUNCTION__); +} + +static void +myri10ge_init(void *arg) +{ +} + + + +static void +myri10ge_free_mbufs(myri10ge_softc_t *sc) +{ + int i; + + for (i = 0; i <= sc->rx_big.mask; i++) { + if (sc->rx_big.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->rx_big.dmat, + sc->rx_big.info[i].map); + m_freem(sc->rx_big.info[i].m); + sc->rx_big.info[i].m = NULL; + } + + for (i = 0; i <= sc->rx_big.mask; i++) { + if (sc->rx_big.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->rx_big.dmat, + sc->rx_big.info[i].map); + m_freem(sc->rx_big.info[i].m); + sc->rx_big.info[i].m = NULL; + } + + for (i = 0; i <= sc->tx.mask; i++) { + if (sc->tx.info[i].m == NULL) + continue; + bus_dmamap_unload(sc->tx.dmat, + sc->tx.info[i].map); + m_freem(sc->tx.info[i].m); + sc->tx.info[i].m = NULL; + } +} + +static void +myri10ge_free_rings(myri10ge_softc_t *sc) +{ + int i; + + + if (sc->rx_small.shadow != NULL) + free(sc->rx_small.shadow, M_DEVBUF); + if (sc->rx_big.shadow != NULL) + free(sc->rx_big.shadow, M_DEVBUF); + if (sc->tx.info != NULL) { + for (i = 0; i <= sc->tx.mask; i++) { + if (sc->tx.info[i].map != NULL) + bus_dmamap_destroy(sc->tx.dmat, + sc->tx.info[i].map); + } + free(sc->tx.info, M_DEVBUF); + } + if (sc->rx_small.info != NULL) { + for (i = 0; i <= sc->rx_small.mask; i++) { + if (sc->rx_small.info[i].map != NULL) + bus_dmamap_destroy(sc->rx_small.dmat, + sc->rx_small.info[i].map); + } + free(sc->rx_small.info, M_DEVBUF); + } + if (sc->rx_big.info != NULL) { + for (i = 0; i <= sc->rx_big.mask; i++) { + if (sc->rx_big.info[i].map != NULL) + bus_dmamap_destroy(sc->rx_big.dmat, + sc->rx_big.info[i].map); + } + free(sc->rx_big.info, M_DEVBUF); + } + if (sc->rx_big.extra_map != NULL) + bus_dmamap_destroy(sc->rx_big.dmat, + sc->rx_big.extra_map); + if (sc->rx_small.extra_map != NULL) + bus_dmamap_destroy(sc->rx_small.dmat, + sc->rx_small.extra_map); + if (sc->tx.dmat != NULL) + bus_dma_tag_destroy(sc->tx.dmat); + if (sc->rx_small.dmat != NULL) + bus_dma_tag_destroy(sc->rx_small.dmat); + if (sc->rx_big.dmat != NULL) + bus_dma_tag_destroy(sc->rx_big.dmat); +} + +static int +myri10ge_alloc_rings(myri10ge_softc_t *sc) +{ + myri10ge_cmd_t cmd; + int tx_ring_size, rx_ring_size; + int tx_ring_entries, rx_ring_entries; + int i, err; + unsigned long bytes; + + /* get ring sizes */ + err = myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_GET_SEND_RING_SIZE, + &cmd); + tx_ring_size = cmd.data0; + err |= myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_GET_RX_RING_SIZE, + &cmd); + if (err != 0) { + device_printf(sc->dev, "Cannot determine ring sizes\n"); + goto abort_with_nothing; + } + + rx_ring_size = cmd.data0; + + tx_ring_entries = tx_ring_size / sizeof (mcp_kreq_ether_send_t); + rx_ring_entries = rx_ring_size / sizeof (mcp_dma_addr_t); + sc->ifp->if_snd.ifq_maxlen = tx_ring_entries - 1; + sc->ifp->if_snd.ifq_drv_maxlen = sc->ifp->if_snd.ifq_maxlen; + + sc->tx.mask = tx_ring_entries - 1; + sc->rx_small.mask = sc->rx_big.mask = rx_ring_entries - 1; + + err = ENOMEM; + /* allocate the rx shadow rings */ + bytes = rx_ring_entries * sizeof (*sc->rx_small.shadow); + sc->rx_small.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_small.shadow == NULL) + goto abort_with_nothing; + + bytes = rx_ring_entries * sizeof (*sc->rx_big.shadow); + sc->rx_big.shadow = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_big.shadow == NULL) + goto abort_with_alloc; + + /* allocate the host info rings */ + bytes = tx_ring_entries * sizeof (*sc->tx.info); + sc->tx.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->tx.info == NULL) + goto abort_with_alloc; + + bytes = rx_ring_entries * sizeof (*sc->rx_small.info); + sc->rx_small.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_small.info == NULL) + goto abort_with_alloc; + + bytes = rx_ring_entries * sizeof (*sc->rx_big.info); + sc->rx_big.info = malloc(bytes, M_DEVBUF, M_ZERO|M_WAITOK); + if (sc->rx_big.info == NULL) + goto abort_with_alloc; + + /* allocate the busdma resources */ + err = bus_dma_tag_create(NULL, /* parent */ + 1, /* alignment */ + sc->tx.boundary, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + MYRI10GE_MAX_ETHER_MTU,/* maxsize */ + MYRI10GE_MCP_ETHER_MAX_SEND_DESC,/* num segs */ + sc->tx.boundary, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->tx.dmat); /* tag */ + + if (err != 0) { + device_printf(sc->dev, "Err %d allocating tx dmat\n", + err); + goto abort_with_alloc; + } + + err = bus_dma_tag_create(NULL, /* parent */ + 1, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + MHLEN, /* maxsize */ + 1, /* num segs */ + MHLEN, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->rx_small.dmat); /* tag */ + if (err != 0) { + device_printf(sc->dev, "Err %d allocating rx_small dmat\n", + err); + goto abort_with_alloc; + } + + err = bus_dma_tag_create(NULL, /* parent */ + 1, /* alignment */ + 4096, /* boundary */ + BUS_SPACE_MAXADDR, /* low */ + BUS_SPACE_MAXADDR, /* high */ + NULL, NULL, /* filter */ + 4096, /* maxsize */ + 1, /* num segs */ + 4096, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lock */ + &sc->rx_big.dmat); /* tag */ + if (err != 0) { + device_printf(sc->dev, "Err %d allocating rx_big dmat\n", + err); + goto abort_with_alloc; + } + + /* now use these tags to setup dmamaps for each slot + in each ring */ + for (i = 0; i <= sc->tx.mask; i++) { + err = bus_dmamap_create(sc->tx.dmat, 0, + &sc->tx.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d tx dmamap\n", + err); + goto abort_with_alloc; + } + } + for (i = 0; i <= sc->rx_small.mask; i++) { + err = bus_dmamap_create(sc->rx_small.dmat, 0, + &sc->rx_small.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d rx_small dmamap\n", + err); + goto abort_with_alloc; + } + } + err = bus_dmamap_create(sc->rx_small.dmat, 0, + &sc->rx_small.extra_map); + if (err != 0) { + device_printf(sc->dev, "Err %d extra rx_small dmamap\n", + err); + goto abort_with_alloc; + } + + for (i = 0; i <= sc->rx_big.mask; i++) { + err = bus_dmamap_create(sc->rx_big.dmat, 0, + &sc->rx_big.info[i].map); + if (err != 0) { + device_printf(sc->dev, "Err %d rx_big dmamap\n", + err); + goto abort_with_alloc; + } + } + err = bus_dmamap_create(sc->rx_big.dmat, 0, + &sc->rx_big.extra_map); + if (err != 0) { + device_printf(sc->dev, "Err %d extra rx_big dmamap\n", + err); + goto abort_with_alloc; + } + return 0; + +abort_with_alloc: + myri10ge_free_rings(sc); + +abort_with_nothing: + return err; +} + +static int +myri10ge_open(myri10ge_softc_t *sc) +{ + myri10ge_cmd_t cmd; + int i, err; + bus_dmamap_t map; + + + err = myri10ge_reset(sc); + if (err != 0) { + device_printf(sc->dev, "failed to reset\n"); + return EIO; + } + + if (MCLBYTES >= + sc->ifp->if_mtu + ETHER_HDR_LEN + MYRI10GE_MCP_ETHER_PAD) + sc->big_bytes = MCLBYTES; + else + sc->big_bytes = MJUM4BYTES; + + err = myri10ge_alloc_rings(sc); + if (err != 0) { + device_printf(sc->dev, "failed to allocate rings\n"); + return err;; + } + + /* get the lanai pointers to the send and receive rings */ + + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_GET_SEND_OFFSET, &cmd); + sc->tx.lanai = + (volatile mcp_kreq_ether_send_t *)(sc->sram + cmd.data0); + err |= myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_GET_SMALL_RX_OFFSET, &cmd); + sc->rx_small.lanai = + (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); + err |= myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_GET_BIG_RX_OFFSET, &cmd); + sc->rx_big.lanai = + (volatile mcp_kreq_ether_recv_t *)(sc->sram + cmd.data0); + + if (err != 0) { + device_printf(sc->dev, + "failed to get ring sizes or locations\n"); + return EIO; + } + + if (sc->wc) { + sc->tx.wc_fifo = sc->sram + 0x200000; + sc->rx_small.wc_fifo = sc->sram + 0x300000; + sc->rx_big.wc_fifo = sc->sram + 0x340000; + } else { + sc->tx.wc_fifo = 0; + sc->rx_small.wc_fifo = 0; + sc->rx_big.wc_fifo = 0; + } + + + /* stock receive rings */ + for (i = 0; i <= sc->rx_small.mask; i++) { + map = sc->rx_small.info[i].map; + err = myri10ge_get_buf_small(sc, map, i); + if (err) { + device_printf(sc->dev, "alloced %d/%d smalls\n", + i, sc->rx_small.mask + 1); + goto abort; + } + } + for (i = 0; i <= sc->rx_big.mask; i++) { + map = sc->rx_big.info[i].map; + err = myri10ge_get_buf_big(sc, map, i); + if (err) { + device_printf(sc->dev, "alloced %d/%d bigs\n", + i, sc->rx_big.mask + 1); + goto abort; + } + } + + /* Give the firmware the mtu and the big and small buffer + sizes. The firmware wants the big buf size to be a power + of two. Luckily, FreeBSD's clusters are powers of two */ + cmd.data0 = sc->ifp->if_mtu + ETHER_HDR_LEN; + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_SET_MTU, &cmd); + cmd.data0 = MHLEN; + err |= myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_SET_SMALL_BUFFER_SIZE, + &cmd); + cmd.data0 = sc->big_bytes; + err |= myri10ge_send_cmd(sc, + MYRI10GE_MCP_CMD_SET_BIG_BUFFER_SIZE, + &cmd); + /* Now give him the pointer to the stats block */ + cmd.data0 = MYRI10GE_LOWPART_TO_U32(sc->fw_stats_dma.bus_addr); + cmd.data1 = MYRI10GE_HIGHPART_TO_U32(sc->fw_stats_dma.bus_addr); + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_SET_STATS_DMA, &cmd); + + if (err != 0) { + device_printf(sc->dev, "failed to setup params\n"); + goto abort; + } + + /* Finally, start the firmware running */ + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_ETHERNET_UP, &cmd); + if (err) { + device_printf(sc->dev, "Couldn't bring up link\n"); + goto abort; + } + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + return 0; + + +abort: + myri10ge_free_mbufs(sc); + myri10ge_free_rings(sc); + return err; +} + +static int +myri10ge_close(myri10ge_softc_t *sc) +{ + myri10ge_cmd_t cmd; + int err, old_down_cnt; + + sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + old_down_cnt = sc->down_cnt; + mb(); + err = myri10ge_send_cmd(sc, MYRI10GE_MCP_CMD_ETHERNET_DOWN, &cmd); + if (err) { + device_printf(sc->dev, "Couldn't bring down link\n"); + } + if (old_down_cnt == sc->down_cnt) { + /* wait for down irq */ + (void)tsleep(&sc->down_cnt, PWAIT, "down myri10ge", hz); + } + if (old_down_cnt == sc->down_cnt) { + device_printf(sc->dev, "never got down irq\n"); + } + myri10ge_free_mbufs(sc); + myri10ge_free_rings(sc); + return 0; +} + + +static int +myri10ge_media_change(struct ifnet *ifp) +{ + return EINVAL; +} + +static int +myri10ge_change_mtu(myri10ge_softc_t *sc, int mtu) +{ + struct ifnet *ifp = sc->ifp; + int real_mtu, old_mtu; + int err = 0; + + + real_mtu = mtu + ETHER_HDR_LEN; + if ((real_mtu > MYRI10GE_MAX_ETHER_MTU) || + real_mtu < 60) + return EINVAL; + sx_xlock(&sc->driver_lock); + old_mtu = ifp->if_mtu; + ifp->if_mtu = mtu; + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + myri10ge_close(sc); + err = myri10ge_open(sc); + if (err != 0) { + ifp->if_mtu = old_mtu; + myri10ge_close(sc); + (void) myri10ge_open(sc); + } + } + sx_xunlock(&sc->driver_lock); + return err; +} + +static void +myri10ge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + myri10ge_softc_t *sc = ifp->if_softc; + + + if (sc == NULL) + return; + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_status |= sc->fw_stats->link_up ? IFM_ACTIVE : 0; + ifmr->ifm_active = IFM_AUTO | IFM_ETHER; + ifmr->ifm_active |= sc->fw_stats->link_up ? IFM_FDX : 0; +} + +static int +myri10ge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + myri10ge_softc_t *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int err, mask; + + err = 0; + switch (command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + err = ether_ioctl(ifp, command, data); + break; + + case SIOCSIFMTU: + err = myri10ge_change_mtu(sc, ifr->ifr_mtu); + break; + + case SIOCSIFFLAGS: + sx_xlock(&sc->driver_lock); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + err = myri10ge_open(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + myri10ge_close(sc); + } + sx_xunlock(&sc->driver_lock); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + err = 0; + break; + + case SIOCSIFCAP: + sx_xlock(&sc->driver_lock); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_TXCSUM) { + if (IFCAP_TXCSUM & ifp->if_capenable) { + ifp->if_capenable &= ~IFCAP_TXCSUM; + ifp->if_hwassist &= ~(CSUM_TCP | CSUM_UDP); + } else { + ifp->if_capenable |= IFCAP_TXCSUM; + ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); + } + } else if (mask & IFCAP_RXCSUM) { + if (IFCAP_RXCSUM & ifp->if_capenable) { + ifp->if_capenable &= ~IFCAP_RXCSUM; + sc->csum_flag &= ~MYRI10GE_MCP_ETHER_FLAGS_CKSUM; + } else { + ifp->if_capenable |= IFCAP_RXCSUM; + sc->csum_flag |= MYRI10GE_MCP_ETHER_FLAGS_CKSUM; + } + } + sx_xunlock(&sc->driver_lock); + break; + + case SIOCGIFMEDIA: + err = ifmedia_ioctl(ifp, (struct ifreq *)data, + &sc->media, command); + break; + + default: + err = ENOTTY; + } + return err; +} + +static void +myri10ge_fetch_tunables(myri10ge_softc_t *sc) +{ + + TUNABLE_INT_FETCH("hw.myri10ge.flow_control_enabled", + &myri10ge_flow_control); + TUNABLE_INT_FETCH("hw.myri10ge.intr_coal_delay", + &myri10ge_intr_coal_delay); + TUNABLE_INT_FETCH("hw.myri10ge.nvidia_ecrc_enable", + &myri10ge_nvidia_ecrc_enable); + TUNABLE_INT_FETCH("hw.myri10ge.skip_pio_read", + &myri10ge_skip_pio_read); + + if (myri10ge_intr_coal_delay < 0 || + myri10ge_intr_coal_delay > 10*1000) + myri10ge_intr_coal_delay = 30; + sc->pause = myri10ge_flow_control; +} + +static int +myri10ge_attach(device_t dev) +{ + myri10ge_softc_t *sc = device_get_softc(dev); + struct ifnet *ifp; + size_t bytes; + int rid, err, i; + uint16_t cmd; + + sc->dev = dev; + myri10ge_fetch_tunables(sc); + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + err = ENOSPC; + goto abort_with_nothing; + } + mtx_init(&sc->cmd_lock, NULL, + MTX_NETWORK_LOCK, MTX_DEF); + mtx_init(&sc->tx_lock, device_get_nameunit(dev), + MTX_NETWORK_LOCK, MTX_DEF); + sx_init(&sc->driver_lock, device_get_nameunit(dev)); + + /* Enable DMA and Memory space access */ + pci_enable_busmaster(dev); + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + cmd |= PCIM_CMD_MEMEN; + pci_write_config(dev, PCIR_COMMAND, cmd, 2); + + /* Map the board into the kernel */ + rid = PCIR_BARS; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, + ~0, 1, RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "could not map memory\n"); + err = ENXIO; + goto abort_with_lock; + } + sc->sram = rman_get_virtual(sc->mem_res); + sc->sram_size = 2*1024*1024 - (2*(48*1024)+(32*1024)) - 0x100; + if (sc->sram_size > rman_get_size(sc->mem_res)) { + device_printf(dev, "impossible memory region size %ld\n", + rman_get_size(sc->mem_res)); + err = ENXIO; + goto abort_with_mem_res; + } + + /* make NULL terminated copy of the EEPROM strings section of + lanai SRAM */ + bzero(sc->eeprom_strings, MYRI10GE_EEPROM_STRINGS_SIZE); + bus_space_read_region_1(rman_get_bustag(sc->mem_res), + rman_get_bushandle(sc->mem_res), + sc->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE, + sc->eeprom_strings, + MYRI10GE_EEPROM_STRINGS_SIZE - 2); + err = myri10ge_parse_strings(sc); + if (err != 0) + goto abort_with_mem_res; + + /* Enable write combining for efficient use of PCIe bus */ + myri10ge_enable_wc(sc); + + /* Allocate the out of band dma memory */ + err = myri10ge_dma_alloc(dev, &sc->cmd_dma, + sizeof (myri10ge_cmd_t), 64); + if (err != 0) + goto abort_with_mem_res; + sc->cmd = (mcp_cmd_response_t *) sc->cmd_dma.addr; + err = myri10ge_dma_alloc(dev, &sc->zeropad_dma, 64, 64); + if (err != 0) + goto abort_with_cmd_dma; + + err = myri10ge_dma_alloc(dev, &sc->fw_stats_dma, + sizeof (*sc->fw_stats), 64); + if (err != 0) + goto abort_with_zeropad_dma; + sc->fw_stats = (mcp_stats_t *)sc->fw_stats_dma.addr; + + + /* allocate interrupt queues */ + bytes = myri10ge_max_intr_slots * sizeof (*sc->intr.q[0]); + for (i = 0; i < MYRI10GE_NUM_INTRQS; i++) { + err = myri10ge_dma_alloc(dev, &sc->intr.dma[i], + bytes, 4096); + if (err != 0) + goto abort_with_intrq; + sc->intr.q[i] = (mcp_slot_t *)sc->intr.dma[i].addr; + } + + /* Add our ithread */ + rid = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, + 1, RF_SHAREABLE | RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "could not alloc interrupt\n"); + goto abort_with_intrq; + } + + err = bus_setup_intr(dev, sc->irq_res, + INTR_TYPE_NET | INTR_MPSAFE, + myri10ge_intr, sc, &sc->ih); + if (err != 0) { + goto abort_with_irq_res; + } + + /* load the firmware */ + myri10ge_select_firmware(sc); + + err = myri10ge_load_firmware(sc); + if (err != 0) + goto abort_with_ithread; + err = myri10ge_reset(sc); + if (err != 0) + goto abort_with_ithread; + + /* hook into the network stack */ + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_baudrate = 100000000; + ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM; + ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + ifp->if_capenable = ifp->if_capabilities; + sc->csum_flag |= MYRI10GE_MCP_ETHER_FLAGS_CKSUM; + ifp->if_init = myri10ge_init; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = myri10ge_ioctl; + ifp->if_start = myri10ge_start; + ifp->if_watchdog = myri10ge_watchdog; + ether_ifattach(ifp, sc->mac_addr); + /* ether_ifattach sets mtu to 1500 */ + ifp->if_mtu = MYRI10GE_MAX_ETHER_MTU - ETHER_HDR_LEN; + + /* Initialise the ifmedia structure */ + ifmedia_init(&sc->media, 0, myri10ge_media_change, + myri10ge_media_status); + ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL); + myri10ge_add_sysctls(sc); + return 0; + +abort_with_ithread: + bus_teardown_intr(dev, sc->irq_res, sc->ih); +abort_with_irq_res: + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); +abort_with_intrq: + for (i = 0; i < MYRI10GE_NUM_INTRQS; i++) { + if (sc->intr.q[i] == NULL) + continue; + sc->intr.q[i] = NULL; + myri10ge_dma_free(&sc->intr.dma[i]); + } + myri10ge_dma_free(&sc->fw_stats_dma); +abort_with_zeropad_dma: + myri10ge_dma_free(&sc->zeropad_dma); +abort_with_cmd_dma: + myri10ge_dma_free(&sc->cmd_dma); +abort_with_mem_res: + bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); +abort_with_lock: + pci_disable_busmaster(dev); + mtx_destroy(&sc->cmd_lock); + mtx_destroy(&sc->tx_lock); + sx_destroy(&sc->driver_lock); + if_free(ifp); +abort_with_nothing: + return err; +} + +static int +myri10ge_detach(device_t dev) +{ + myri10ge_softc_t *sc = device_get_softc(dev); + int i; + + sx_xlock(&sc->driver_lock); + if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) + myri10ge_close(sc); + sx_xunlock(&sc->driver_lock); + if (sc->ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->ih); + ether_ifdetach(sc->ifp); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + for (i = 0; i < MYRI10GE_NUM_INTRQS; i++) { + if (sc->intr.q[i] == NULL) + continue; + sc->intr.q[i] = NULL; + myri10ge_dma_free(&sc->intr.dma[i]); + } + myri10ge_dma_free(&sc->fw_stats_dma); + myri10ge_dma_free(&sc->zeropad_dma); + myri10ge_dma_free(&sc->cmd_dma); + bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BARS, sc->mem_res); + pci_disable_busmaster(dev); + mtx_destroy(&sc->cmd_lock); + mtx_destroy(&sc->tx_lock); + sx_destroy(&sc->driver_lock); + if_free(sc->ifp); + return 0; +} + +static int +myri10ge_shutdown(device_t dev) +{ + return 0; +} + +/* + This file uses Myri10GE driver indentation. + + Local Variables: + c-file-style:"linux" + tab-width:8 + End: +*/ Index: sys/dev/myri10ge/if_myri10ge_var.h =================================================================== RCS file: sys/dev/myri10ge/if_myri10ge_var.h diff -N sys/dev/myri10ge/if_myri10ge_var.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/myri10ge/if_myri10ge_var.h 7 Feb 2006 15:38:49 -0000 @@ -0,0 +1,169 @@ + +#define MYRI10GE_MAX_ETHER_MTU 9014 + +#define MYRI10GE_ETH_STOPPED 0 +#define MYRI10GE_ETH_STOPPING 1 +#define MYRI10GE_ETH_STARTING 2 +#define MYRI10GE_ETH_RUNNING 3 +#define MYRI10GE_ETH_OPEN_FAILED 4 + +#define MYRI10GE_FW_OFFSET 1024*1024 +#define MYRI10GE_EEPROM_STRINGS_SIZE 256 +#define MYRI10GE_NUM_INTRQS 2 + +typedef struct { + void *addr; + bus_addr_t bus_addr; + bus_dma_tag_t dmat; + bus_dmamap_t map; +} myri10ge_dma_t; + +typedef struct myri10ge_intrq +{ + mcp_slot_t *q[2]; + int intrq; + int slot; + int maxslots; + uint32_t seqnum; + uint32_t spurious; + uint32_t cnt; + myri10ge_dma_t dma[2]; +} myri10ge_intrq_t; + + +typedef struct +{ + uint32_t data0; + uint32_t data1; + uint32_t data2; +} myri10ge_cmd_t; + +struct myri10ge_buffer_state { + struct mbuf *m; + bus_dmamap_t map; +}; + +typedef struct +{ + volatile mcp_kreq_ether_recv_t *lanai; /* lanai ptr for recv ring */ + volatile uint8_t *wc_fifo; /* w/c rx dma addr fifo address */ + mcp_kreq_ether_recv_t *shadow; /* host shadow of recv ring */ + struct myri10ge_buffer_state *info; + bus_dma_tag_t dmat; + bus_dmamap_t extra_map; + int cnt; + int alloc_fail; + int mask; /* number of rx slots -1 */ +} myri10ge_rx_buf_t; + +typedef struct +{ + volatile mcp_kreq_ether_send_t *lanai; /* lanai ptr for sendq */ + volatile uint8_t *wc_fifo; /* w/c send fifo address */ + mcp_kreq_ether_send_t *shadow; /* host shadow of sendq */ + struct myri10ge_buffer_state *info; + bus_dma_tag_t dmat; + int req; /* transmits submitted */ + int mask; /* number of transmit slots -1 */ + int done; /* transmits completed */ + int boundary; /* boundary transmits cannot cross*/ +} myri10ge_tx_buf_t; + +typedef struct { + struct ifnet* ifp; + int big_bytes; + struct mtx tx_lock; + int csum_flag; /* rx_csums? */ + uint8_t mac_addr[6]; /* eeprom mac address */ + myri10ge_tx_buf_t tx; /* transmit ring */ + myri10ge_rx_buf_t rx_small; + myri10ge_rx_buf_t rx_big; + bus_dma_tag_t meta_tag; + volatile uint8_t *sram; + int sram_size; + unsigned long board_span; + unsigned long iomem_base; + volatile uint32_t *irq_claim; + char *mac_addr_string; + char *product_code_string; + mcp_cmd_response_t *cmd; + myri10ge_dma_t cmd_dma; + myri10ge_dma_t zeropad_dma; + mcp_stats_t *fw_stats; + myri10ge_dma_t fw_stats_dma; + struct pci_dev *pdev; + int msi_enabled; + myri10ge_intrq_t intr; + int link_state; + int intr_coal_delay; + int wc; + struct mtx cmd_lock; + struct sx driver_lock; + int wake_queue; + int stop_queue; + int down_cnt; + int watchdog_resets; + int tx_defragged; + int pause; + struct resource *mem_res; + struct resource *irq_res; + void *ih; + char *fw_name; + char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE]; + char fw_version[128]; + device_t dev; + struct ifmedia media; + +} myri10ge_softc_t; + +#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1 +#define MYRI10GE_PCI_DEVICE_Z8E 0x0008 + +#define MYRI10GE_HIGHPART_TO_U32(X) \ +(sizeof (X) == 8) ? ((uint32_t)((uint64_t)(X) >> 32)) : (0) +#define MYRI10GE_LOWPART_TO_U32(X) ((uint32_t)(X)) + + +/* implement our own memory barriers, since bus_space_barrier + cannot handle write-combining regions */ + +#if defined (__GNUC__) + #if #cpu(i386) || defined __i386 || defined i386 || defined __i386__ || #cpu(x86_64) || defined __x86_64__ + #define mb() __asm__ __volatile__ ("sfence;": : :"memory") + #elif #cpu(sparc64) || defined sparc64 || defined __sparcv9 + #define mb() __asm__ __volatile__ ("membar #MemIssue": : :"memory") + #elif #cpu(sparc) || defined sparc || defined __sparc__ + #define mb() __asm__ __volatile__ ("stbar;": : :"memory") + #else + #define mb() /* XXX just to make this compile */ + #endif +#else + #error "unknown compiler" +#endif + +static inline void +myri10ge_pio_copy(volatile void *to_v, void *from_v, size_t size) +{ + register volatile uintptr_t *to; + volatile uintptr_t *from; + size_t i; + + to = (volatile uintptr_t *) to_v; + from = from_v; + for (i = (size / sizeof (uintptr_t)); i; i--) { + *to = *from; + to++; + from++; + } + +} + + +/* + This file uses Myri10GE driver indentation. + + Local Variables: + c-file-style:"linux" + tab-width:8 + End: +*/ Index: sys/dev/myri10ge/mcp_gen_header.h =================================================================== RCS file: sys/dev/myri10ge/mcp_gen_header.h diff -N sys/dev/myri10ge/mcp_gen_header.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/myri10ge/mcp_gen_header.h 7 Feb 2006 15:16:19 -0000 @@ -0,0 +1,73 @@ +#ifndef _mcp_gen_header_h +#define _mcp_gen_header_h + +/* this file define a standard header used as a first entry point to + exchange information between firmware/driver and driver. The + header structure can be anywhere in the mcp. It will usually be in + the .data section, because some fields needs to be initialized at + compile time. + The 32bit word at offset MX_HEADER_PTR_OFFSET in the mcp must + contains the location of the header. + + Typically a MCP will start with the following: + .text + .space 52 ! to help catch MEMORY_INT errors + bt start ! jump to real code + nop + .long _gen_mcp_header + + The source will have a definition like: + + mcp_gen_header_t gen_mcp_header = { + .header_length = sizeof(mcp_gen_header_t), + .mcp_type = MCP_TYPE_XXX, + .version = "something $Id: mcp_gen_header.h,v 1.1 2005/12/23 02:10:44 gallatin Exp $", + .mcp_globals = (unsigned)&Globals + }; +*/ + + +#define MCP_HEADER_PTR_OFFSET 0x3c + +#define MCP_TYPE_MX 0x4d582020 /* "MX " */ +#define MCP_TYPE_PCIE 0x70636965 /* "PCIE" pcie-only MCP */ +#define MCP_TYPE_ETH 0x45544820 /* "ETH " */ +#define MCP_TYPE_MCP0 0x4d435030 /* "MCP0" */ + + +typedef struct mcp_gen_header { + /* the first 4 fields are filled at compile time */ + unsigned header_length; + unsigned mcp_type; + char version[128]; + unsigned mcp_globals; /* pointer to mcp-type specific structure */ + + /* filled by the MCP at run-time */ + unsigned sram_size; + unsigned string_specs; /* either the original STRING_SPECS or a superset */ + unsigned string_specs_len; + + /* Fields above this comment are guaranteed to be present. + + Fields below this comment are extensions added in later versions + of this struct, drivers should compare the header_length against + offsetof(field) to check wether a given MCP implements them. + + Never remove any field. Keep everything naturally align. + */ +} mcp_gen_header_t; + +/* Macro to create a simple mcp header */ +#define MCP_GEN_HEADER_DECL(type, version_str, global_ptr) \ + struct mcp_gen_header mcp_gen_header = { \ + sizeof (struct mcp_gen_header), \ + (type), \ + version_str, \ + (global_ptr), \ + SRAM_SIZE, \ + (unsigned int) STRING_SPECS, \ + 256 \ + } + + +#endif /* _mcp_gen_header_h */ Index: sys/dev/myri10ge/myri10ge_mcp.h =================================================================== RCS file: sys/dev/myri10ge/myri10ge_mcp.h diff -N sys/dev/myri10ge/myri10ge_mcp.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/myri10ge/myri10ge_mcp.h 7 Feb 2006 15:16:36 -0000 @@ -0,0 +1,226 @@ +#ifndef _myri10ge_mcp_h +#define _myri10ge_mcp_h + +#ifdef MYRI10GE_MCP +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#endif + +/* 8 Bytes */ +typedef struct +{ + uint32_t high; + uint32_t low; +} mcp_dma_addr_t; + +/* 16 Bytes */ +typedef struct +{ + uint32_t data0; + uint32_t data1; + uint32_t seqnum; + uint16_t index; + uint8_t flag; + uint8_t type; +} mcp_slot_t; + +/* 64 Bytes */ +typedef struct +{ + uint32_t cmd; + uint32_t data0; /* will be low portion if data > 32 bits */ + /* 8 */ + uint32_t data1; /* will be high portion if data > 32 bits */ + uint32_t data2; /* currently unused.. */ + /* 16 */ + mcp_dma_addr_t response_addr; + /* 24 */ + uint8_t pad[40]; +} mcp_cmd_t; + +/* 8 Bytes */ +typedef struct +{ + uint32_t data; + uint32_t result; +} mcp_cmd_response_t; + + + +/* + flags used in mcp_kreq_ether_send_t: + + The SMALL flag is only needed in the first segment. It is raised + for packets that are total less or equal 512 bytes. + + The CKSUM flag must be set in all segments. + + The PADDED flags is set if the packet needs to be padded, and it + must be set for all segments. + + The MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD must be set if the cumulative + length of all previous segments was odd. +*/ + + +#define MYRI10GE_MCP_ETHER_FLAGS_VALID 0x1 +#define MYRI10GE_MCP_ETHER_FLAGS_FIRST 0x400 +#define MYRI10GE_MCP_ETHER_FLAGS_NOT_LAST 0x800 +#define MYRI10GE_MCP_ETHER_FLAGS_SMALL 0x1000 +#define MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD 0x2000 +#define MYRI10GE_MCP_ETHER_FLAGS_CKSUM 0x4000 + + +typedef union mcp_pso_or_cumlen +{ + uint16_t pseudo_hdr_offset; + uint16_t cum_len; +} mcp_pso_or_cumlen_t; + +#define MYRI10GE_MCP_ETHER_MAX_SEND_DESC 12 +#define MYRI10GE_MCP_ETHER_PAD 2 + +/* 16 Bytes */ +typedef struct +{ + uint32_t addr_high; + uint32_t addr_low; + uint16_t length; + uint8_t pad; + uint8_t cksum_offset; /* where to start computing cksum */ + uint16_t pseudo_hdr_offset; + uint16_t flags; /* as defined above */ +} mcp_kreq_ether_send_t; + +/* 8 Bytes */ +typedef struct +{ + uint32_t addr_high; + uint32_t addr_low; +} mcp_kreq_ether_recv_t; + + +/* Commands */ + +#define MYRI10GE_MCP_CMD_OFFSET 0xf80000 + +typedef enum { + MYRI10GE_MCP_CMD_NONE = 0, + /* Reset the mcp, it is left in a safe state, waiting + for the driver to set all its parameters */ + MYRI10GE_MCP_CMD_RESET, + + /* get the version number of the current firmware.. + (may be available in the eeprom strings..? */ + MYRI10GE_MCP_GET_MCP_VERSION, + + + /* Parameters which must be set by the driver before it can + issue MYRI10GE_MCP_CMD_ETHERNET_UP. They persist until the next + MYRI10GE_MCP_CMD_RESET is issued */ + + MYRI10GE_MCP_CMD_SET_INTRQ0_DMA, + MYRI10GE_MCP_CMD_SET_INTRQ1_DMA, + MYRI10GE_MCP_CMD_SET_BIG_BUFFER_SIZE, /* in bytes, power of 2 */ + MYRI10GE_MCP_CMD_SET_SMALL_BUFFER_SIZE, /* in bytes */ + + + /* Parameters which refer to lanai SRAM addresses where the + driver must issue PIO writes for various things */ + + MYRI10GE_MCP_CMD_GET_SEND_OFFSET, + MYRI10GE_MCP_CMD_GET_SMALL_RX_OFFSET, + MYRI10GE_MCP_CMD_GET_BIG_RX_OFFSET, + MYRI10GE_MCP_CMD_GET_IRQ_ACK_OFFSET, + MYRI10GE_MCP_CMD_GET_IRQ_DEASSERT_OFFSET, + MYRI10GE_MCP_CMD_GET_IRQ_ACK_DEASSERT_OFFSET, + + /* Parameters which refer to rings stored on the MCP, + and whose size is controlled by the mcp */ + + MYRI10GE_MCP_CMD_GET_SEND_RING_SIZE, /* in bytes */ + MYRI10GE_MCP_CMD_GET_RX_RING_SIZE, /* in bytes */ + + /* Parameters which refer to rings stored in the host, + and whose size is controlled by the host. Note that + all must be physically contiguous and must contain + a power of 2 number of entries. */ + + MYRI10GE_MCP_CMD_SET_INTRQ_SIZE, /* in bytes */ + + /* command to bring ethernet interface up. Above parameters + (plus mtu & mac address) must have been exchanged prior + to issuing this command */ + MYRI10GE_MCP_CMD_ETHERNET_UP, + + /* command to bring ethernet interface down. No further sends + or receives may be processed until an MYRI10GE_MCP_CMD_ETHERNET_UP + is issued, and all interrupt queues must be flushed prior + to ack'ing this command */ + + MYRI10GE_MCP_CMD_ETHERNET_DOWN, + + /* commands the driver may issue live, without resetting + the nic. Note that increasing the mtu "live" should + only be done if the driver has already supplied buffers + sufficiently large to handle the new mtu. Decreasing + the mtu live is safe */ + + MYRI10GE_MCP_CMD_SET_MTU, + MYRI10GE_MCP_CMD_SET_INTR_COAL_DELAY, /* in microseconds */ + MYRI10GE_MCP_CMD_SET_STATS_INTERVAL, /* in microseconds */ + MYRI10GE_MCP_CMD_SET_STATS_DMA, + + MYRI10GE_MCP_ENABLE_PROMISC, + MYRI10GE_MCP_DISABLE_PROMISC, + MYRI10GE_MCP_SET_MAC_ADDRESS, + + MYRI10GE_MCP_ENABLE_FLOW_CONTROL, + MYRI10GE_MCP_DISABLE_FLOW_CONTROL +} myri10ge_mcp_cmd_type_t; + + +typedef enum { + MYRI10GE_MCP_CMD_OK = 0, + MYRI10GE_MCP_CMD_UNKNOWN, + MYRI10GE_MCP_CMD_ERROR_RANGE, + MYRI10GE_MCP_CMD_ERROR_BUSY, + MYRI10GE_MCP_CMD_ERROR_EMPTY, + MYRI10GE_MCP_CMD_ERROR_CLOSED, + MYRI10GE_MCP_CMD_ERROR_HASH_ERROR, + MYRI10GE_MCP_CMD_ERROR_BAD_PORT, + MYRI10GE_MCP_CMD_ERROR_RESOURCES +} myri10ge_mcp_cmd_status_t; + +typedef enum { + MYRI10GE_MCP_INTR_NONE = 0, + MYRI10GE_MCP_INTR_ETHER_SEND_DONE, + MYRI10GE_MCP_INTR_ETHER_RECV_SMALL, + MYRI10GE_MCP_INTR_ETHER_RECV_BIG, + MYRI10GE_MCP_INTR_LINK_CHANGE, + MYRI10GE_MCP_INTR_STATS_UPDATE, + MYRI10GE_MCP_INTR_ETHER_DOWN +} myri10ge_mcp_intr_type_t; + + +/* 32 Bytes */ +typedef struct +{ + uint32_t link_up; + uint32_t dropped_link_overflow; + uint32_t dropped_link_error_or_filtered; + uint32_t dropped_runt; + uint32_t dropped_overrun; + uint32_t dropped_no_small_buffer; + uint32_t dropped_no_big_buffer; + uint32_t dropped_interrupt_busy; +} mcp_stats_t; + + +#endif /* _myri10ge_mcp_h */ Index: sys/conf/files.amd64 =================================================================== RCS file: /home/ncvs/src/sys/conf/files.amd64,v retrieving revision 1.81 diff -u -r1.81 files.amd64 --- sys/conf/files.amd64 24 Jan 2006 21:13:50 -0000 1.81 +++ sys/conf/files.amd64 9 Feb 2006 15:53:13 -0000 @@ -169,6 +169,7 @@ dev/hwpmc/hwpmc_x86.c optional hwpmc dev/kbd/kbd.c optional atkbd | sc | ukbd dev/mem/memutil.c optional mem +dev/myri10ge/if_myri10ge.c optional myri10ge firmware dev/nve/if_nve.c optional nve pci dev/ppc/ppc.c optional ppc dev/ppc/ppc_puc.c optional ppc puc Index: sys/conf/files.i386 =================================================================== RCS file: /home/ncvs/src/sys/conf/files.i386,v retrieving revision 1.551 diff -u -r1.551 files.i386 --- sys/conf/files.i386 31 Jan 2006 23:11:34 -0000 1.551 +++ sys/conf/files.i386 9 Feb 2006 15:53:47 -0000 @@ -188,6 +188,7 @@ dev/mem/memutil.c optional mem dev/mse/mse.c optional mse dev/mse/mse_isa.c optional mse isa +dev/myri10ge/if_myri10ge.c optional myri10ge firmware dev/nve/if_nve.c optional nve pci dev/pcf/pcf_isa.c optional pcf dev/ppc/ppc.c optional ppc Index: sys/modules/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/Makefile,v retrieving revision 1.474 diff -u -r1.474 Makefile --- sys/modules/Makefile 5 Feb 2006 17:38:28 -0000 1.474 +++ sys/modules/Makefile 9 Feb 2006 15:51:13 -0000 @@ -157,6 +157,7 @@ msdosfs_iconv \ ${_mse} \ my \ + ${_myri10ge} \ ${_ncp} \ ${_ncv} \ ${_ndis} \ @@ -352,6 +353,7 @@ _linux= linux _lnc= lnc _mse= mse +_myri10ge= myri10ge _ncp= ncp _ncv= ncv _ndis= ndis @@ -451,6 +453,7 @@ _ips= ips #_lnc= lnc _mly= mly +_myri10ge= myri10ge _ndis= ndis _nve= nve _safe= safe Index: sys/modules/myri10ge/Makefile =================================================================== RCS file: sys/modules/myri10ge/Makefile diff -N sys/modules/myri10ge/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/myri10ge/Makefile 9 Feb 2006 15:52:02 -0000 @@ -0,0 +1,7 @@ +# $FreeBSD$ + +SUBDIR= myri10ge +SUBDIR+=myri10ge_eth_z8e +SUBDIR+=myri10ge_ethp_z8e + +.include Index: sys/modules/myri10ge/myri10ge_eth_z8e/Makefile =================================================================== RCS file: sys/modules/myri10ge/myri10ge_eth_z8e/Makefile diff -N sys/modules/myri10ge/myri10ge_eth_z8e/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/myri10ge/myri10ge_eth_z8e/Makefile 7 Feb 2006 15:26:09 -0000 @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/myri10ge +KMOD= myri10ge_eth_z8e +FIRMWS= eth_z8e.dat:myri10ge_eth_z8e + +.include Index: sys/modules/myri10ge/myri10ge_ethp_z8e/Makefile =================================================================== RCS file: sys/modules/myri10ge/myri10ge_ethp_z8e/Makefile diff -N sys/modules/myri10ge/myri10ge_ethp_z8e/Makefile --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/modules/myri10ge/myri10ge_ethp_z8e/Makefile 7 Feb 2006 15:28:12 -0000 @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/myri10ge +KMOD= myri10ge_ethp_z8e +FIRMWS= ethp_z8e.dat:myri10ge_ethp_z8e + +.include